原创

Java多线程基础(一)


概述

说到线程,不得不先说一下进程。进程是指程序的一次执行过程,在电脑上运行一个程序,就代表开启了一个进程,而线程则是进程中的一个执行单位;总的来说就是一个程序由一个或多个进程组成,一个进程由一个或多个线程组成。

线程的生命周期及状态

关于Java中线程的生命周期,可看下图:

img

线程的五种基本状态:

  • 新建状态(new):当线程对象被创建后,就进入了新建状态

  • 就绪状态(Runnable):当线程的start()方法被调用,线程就会进入就绪状态。此时线程并没有执行,只是做好了热身,需要等待CPU的调度;

  • 运行状态(Running):当处于就绪状态的线程被CPU调度时,线程才真正的开始执行,此时线程就进入了运行状态,需要注意的是就绪状态是进入运行状态的唯一入口,也就是说,想要线程进入运行状态,则必须先处于就绪状态

  • 阻塞状态(Blocked):当处于运行状态的线程因为某种原因,暂时放弃对CPU的使用权,停止执行,此时线程会进入阻塞状态;直到其再次进入就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

    (1)等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

    (2)同步阻塞:线程在获取synchronized同步锁时失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

    (3)其他阻塞:通过调用线程的sleep()join()方法或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态

  • 死亡状态(Dead):线程执行完毕或者因异常退出了run()方法,该线程结束生命周期,进入死亡状态

线程实现

Java中,我们想开启一个线程,常见的方式有以下两种:

1)继承Thread类,重写run方法:

class MyThread extends Thread{
    @Override
    public void run() {
        for (int i=0;i<=15;i++ ){
        System.out.println(Thread.currentThread().getName()+"--"+i);//获取当前线程名字
        }
    }
}
public static void main(String[] args) {
        for (int i=0;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+"---"+i);
            if(i==5){
                //创建新线程,线程进入新建状态
                Thread t1 = new MyThread();
                Thread t2 = new MyThread();
                //这里调用run只会用主线程执行一次对象线程的run方法,并不会使其进入就绪状态
                t1.run();
                //调用start()方法,线程进入就绪状态
                t1.start();
                t2.start();
            }
        }
    }

如上所示,继承Thread类,通过重写run()方法定义了一个新的线程类MyThread,其中run()方法的方法体代表了线程需要执行的任务,称之为线程执行体。当创建此线程类对象时一个新的线程得以创建,并进入到线程新建状态。通过调用线程对象引用的start()方法,使得该线程进入到就绪状态,此时此线程并不一定会马上得以执行,这取决于CPU调度时机。

2)实现Runnable接口,重写run方法,该run()方法同样是线程执行体,创建Runnable实现类的实例,并以此实例作为Thread类构造参数target来创建Thread对象,该Thread对象才是真正的线程对象:

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<=15;i++ ){
            System.out.println(Thread.currentThread().getName()+"--"+i);
        }
    }
}
public static void main(String[] args) {
        for (int i=0;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+"---"+i);
            if(i==5){
                //创建Runnable实现类对象
                Runnable myRunnable = new MyRunnable();
                //将myRunnable作为Thread构造方法的参数传入,线程进入新建状态
                //这里传入的第二个参数"myRunnable_1"是自定义的线程名称
                Thread r1 = new Thread(myRunnable,"myRunnable_1");
                Thread r2 = new Thread(myRunnable,"myRunnable_2");
                //调用start,线程就绪,等待CPU随时调度执行
                r1.start();
                r2.start();
            }
        }
    }

以上就是线程常用的两种创建方式,那么我们日常中应该选择哪种方式来创建线程呢?我们知道在Java中类只能有一个父类,继承了Thread类后就无法再继承其他类,相比较来说实现Runnable接口的方式显然更加灵活,所以答案一目了然。

参考文章:https://www.cnblogs.com/lwbqqyumidi/p/3804883.html

正文到此结束