美文网首页
多线程系列(一)--线程基础

多线程系列(一)--线程基础

作者: stellaYdc | 来源:发表于2018-01-09 14:42 被阅读0次

    文章目录
    (一)线程的定义
    (二)实现多线程的方式

    • 继承Thread类
    • 实现Runnable接口

    (三)Thread常用方法介绍

    • 中断线程方法
      1. interrupt()方法
    • 静态方法{#3.2}
      1. currentThread()方法
      2. sleep()方法
      3. yield()方法
    • 对象方法
      1. isAlive()方法
      2. join()方法
      3. join(long)方法与sleep(long)方法的区别

    (四)停止线程

    • 安全的终止线程
      1. 中断法+boolean变量法
      2. 抛出异常法+return法

    (五)线程优先级
    (六)守护线程(Daemon线程)
    (七)线程的状态(线程的生命周期)

    线程的定义

    线程可以理解成是在进程中独立运行的子任务。

    实现多线程的方式

    1. 继承Thread类;
    2. 实现Runnable接口;

    继承Thread类

    public class Thread implements Runnable
    

    Thread类实现了Runnable接口,他们之间具有多态关系。

    下面是一个创建线程实例,继承Thread类,并且重写run方法。

    public class MyThread extends Thread {
    
        @Override
    
        public void run(){
    
            super.run();
    
            System.out.println("MyThread");
    
        }
    
        public static void main(String[]args){
    
            MyThread myThread=new MyThread();
    
            myThread.start();
    
            System.out.println("main running end!");
    
        }
    
    }
    

    运行结果:

    main running end!
    
    MyThread
    

    从运行结果可以看出,myThread.run()方法执行的时间比较晚,说明在使用多线程技术时,代码运行的结果与代码执行的顺序无关。

    实现Runnable接口

    首先,我们来查看一下Thread.java的构造函数:


    Image.png

    通过实现Runnable接口,也可以创建线程,将Runnable对象通过Thread类的构造函数传进去,下面展示实例:

    public class MyRunnable implements Runnable {
    
        @Override
    
        public void run() {
    
            System.out.println("running!");
    
        }
    
        public static void main(String[]args){
    
            Runnable runnable=new MyRunnable();
    
            Thread thread = new Thread(runnable);
    
            thread.start();
    
            System.out.println("main running end!");
    
        }
    
    }
    

    运行结果:

    main running end!
    
    running!
    

    因为Java只支持单继承,为了避免这一局限性,可以使用实现Runnable接口的方式来实现多线程技术。

    从构造函数Thread(Runnable target)可以看出,我们不光可以传入实现Runnable接口的对象,也可以传入一个Thread类的对象,因为Thread类也实现了Runnable接口,这样做可以将Thraed对象中的run()方法完全交由其他的线程来进行调用。

    Thread常用方法介绍

    方法名称 方法说明
    public synchronized void start() 启动一个线程,Java虚拟机JVM调用run()方法
    public final native boolean isAlive(); isAlive()方法是判断当前线程是否处于活动状态。活动状态是线程已经启动且尚未终止。
    public void interrupt() 中断线程
    public boolean isInterrupted() 测试线程Thread对象是否中断,但是不会清除中断状态,即再次调用this.isInterrupted()方法,返回true.
    public final synchronized void join(long millis);
    public final void join();
    public final synchronized void join(long millis, int nanos);
    如果线程A执行了thread.join()语句,则当前线程A等待thread线程终止之后才从thread.join()返回。两个超时方法表示,如果线程thread在给定的超时时间里没有终止,那么将会从超时方法中返回。
    public static boolean interrupted() 测试当前线程是否中断,执行后会清除中断状态(中断状态会置为false),即第一次调用返回true,再次调用Thread.interrupted()方法,返回false.
    public static native Thread currentThread(); 返回对当前正在执行的线程对象的引用。
    public static native void sleep(long millis);
    public static void sleep(long millis, int nanos)
    sleep()方法是让当前线程“正在执行的线程”休眠(暂停)指定的毫秒数。
    public static native void yield(); 让当前线程放弃当前的CPU资源,将CPU让给其他的任务去占用CPU执行时间。由running状态变为ready状态

    中断线程方法

    方法名称 方法说明
    public void interrupt() 中断线程
    public boolean isInterrupted() 测试线程Thread对象是否中断,但是不会清除中断状态,即再次调用this.isInterrupted()方法,返回true.
    public static boolean interrupted() 测试当前线程是否中断,执行后会清除中断状态(中断状态会置为false),即第一次调用返回true,再次调用Thread.interrupted()方法,返回false.

    中断可以理解为线程的一个标识位属性,它表示一个运行中线程是否被其他线程进行了中断操作。其他线程通过调用该线程的interrupt()方法来对它进行中断操作。

    interrupt()方法

    interrupt()方法的作用是中断线程。对象方法。

    方法说明:

    1.如果被中断的线程正在调用Object.wait()/Object.wait(long)/Object.wait(long,int),Thread.join()/Thread.join(long)/Thread.join(long,int),Thread.sleep(long)/Thread.sleep(long,int),那么中断状态将会被清除(中断状态置为false),并且抛出InterruptedException异常。

    举例说明:

    public class InterruptThread{
    
        public static void main(String[]args){
    
            try{
    
                SleepThread sleepThread=new SleepThread();
    
                BusyThread busyThread=new BusyThread();
    
                sleepThread.start();
    
                busyThread.start();
    
                Thread.sleep(1000);
    
                sleepThread.interrupt();
    
                busyThread.interrupt();
    
                System.out.println("busyThread isInterrupted="+busyThread.isInterrupted());
    
                System.out.println("busyThread isInterrupted="+busyThread.isInterrupted());
    
                System.out.println("sleepThread isInterrupted="+sleepThread.isInterrupted());
    
                System.out.println("sleepThread isInterrupted="+sleepThread.isInterrupted());
    
                Thread.currentThread().interrupt();
    
                System.out.println("main interrupted="+Thread.interrupted());
    
                System.out.println("main interrupted="+Thread.interrupted());
    
            } catch (InterruptedException e) {
    
                e.printStackTrace();
    
            }
    
        }
    
        public static class SleepThread extends Thread{
    
            @Override
    
            public void run(){
    
                try {
    
                    Thread.sleep(5000);
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                }
    
            }
    
        }
    
        public static class BusyThread extends Thread{
    
            @Override
    
            public void run(){
    
                while (true){
    
                }
    
            }
    
        }
    
    }
    

    运行结果:

    busyThread isInterrupted=true
    
    busyThread isInterrupted=true
    
    sleepThread isInterrupted=false
    
    sleepThread isInterrupted=false
    
    main interrupted=true
    
    main interrupted=false
    
    java.lang.InterruptedException: sleep interrupted
    
        at java.lang.Thread.sleep(Native Method)
    
        at JavaMultiThreadProgramming.InterruptThread$SleepThread.run(InterruptThread.java:31)
    

    结果分析:

    busyThread线程与main线程结果对比验证:

    busyThread线程中断之后,调用Thread的对象方法isInterrupted()获取busyThread线程的中断状态为true,再次调用还是为true,说明isInterrupted()方法不会重置中断标志位。

    main线程中断之后,调用Thread.interrupted()方法,第一次返回true,第二次返回false,验证第一次调用Thread.interrupted()方法之后会重置该线程的中断标志位,因此第二次调用的时候返回结果为false.

    sleepThread线程与busyThread线程结果对比:

    busyThread线程一直在运行,中断之后,线程正常中断,而sleepThread线程一直在睡眠,中断之后会抛出InterruptedException异常;

    sleepThread线程中断之后调用interrupted()方法,两次都返回false,验证在该线程正在sleep()的时候中断线程,在抛出异常之前会先清除中断标志位,即中断标识置为false。

    静态方法

    currentThread()方法

    currentThread()方法返回代码段正在被哪个线程调用。静态方法。
    JDK中实现:

    public static native Thread currentThread();
    

    举例说明:

    public class Run1 {
    
        public static void main(String[]args){
    
            System.out.println(Thread.currentThread().getName());
    
        }
    
    }
    

    运行结果:

    main
    

    结果说明:main()方法被名为main的线程调用。

    再次举例说明:

    public class Run1 extends Thread{
    
        public Run1(){
    
            System.out.println("Run1 printed by "+Thread.currentThread().getName());
    
        }
    
        @Override
    
        public void run(){
    
            System.out.println("run printed by "+Thread.currentThread().getName());
    
        }
    
        public static void main(String[]args){
    
            System.out.println("main printed by "+Thread.currentThread().getName());
    
            Run1 run1=new Run1();
    
            run1.start();
    
            //run1.run();
    
        }
    
    }
    

    运行结果:

    main printed by main
    
    Run1 printed by main
    
    run printed by Thread-0
    

    结果说明:

    Run1的构造方法被main线程调用,run方法被thread-0的线程调用,说明run1.start()方法会新启一个线程。将上述代码中的run1.run()的注释取消,再次执行,得到的结果如下:

    main printed by main
    
    Run1 printed by main
    
    run printed by main
    
    run printed by Thread-0
    

    从上面的结果可以看出,直接调用线程的run方法run1.run(),该方法是由main线程来执行的,并没有新启一个线程。

    sleep()方法

    sleep()方法是让当前线程“正在执行的线程”休眠(暂停)指定的毫秒数。“正在执行的线程”指的是this.currentThread()返回的线程。

    JDK实现:

    public static native void sleep(long millis) throws InterruptedException;
    

    方法说明:

    1. 暂停线程,线程进入到waiting状态,会让出CPU。
    2. 不会放弃对象锁。
    3. 静态方法,作用在当前线程上。

    举例说明:

    public class Run3 extends Thread {
    
        @Override
    
        public void run(){
    
            try {
    
                System.out.println("run threadName="+this.currentThread().getName()+" begin "+System.currentTimeMillis());
    
                Thread.sleep(2000);
    
                System.out.println("run threadName="+this.currentThread().getName()+" end "+System.currentTimeMillis());
    
            } catch (InterruptedException e) {
    
                e.printStackTrace();
    
            }
    
        }
    
        public static void main(String[]args) throws InterruptedException {
    
            Run3 run3=new Run3();
    
            run3.start();
    
            System.out.println("main "+" begin "+System.currentTimeMillis());
    
            System.out.println("main "+" end "+System.currentTimeMillis());
    
        }
    
    }
    

    运行结果:

    main  begin 1515381264226
    main  end 1515381264226
    
    run threadName=Thread-0 begin 1515381264226
    run threadName=Thread-0 end 1515381266219
    

    结果说明:因为main线程与Run3线程是异步执行的,所以main线程可能先执行完,打印了main的begin和end,Run3线程随后执行,打印run begin和run end.

    yield()方法

    yield()方法让当前线程放弃当前的CPU资源,将CPU让给其他的任务去占用CPU执行时间。由running状态变为ready状态。静态方法。但是放弃的时间不确定,有可能刚刚放弃,马上又获得了CPU时间片。

    JDK实现:

    public static native void yield();
    

    举例说明:

    public class YieldThread extends Thread{
    
        @Override
    
        public void run(){
    
            long begin=System.currentTimeMillis();
    
            long sum=0;
    
            for(int i=0;i<50000000;i++){
    
                //Thread.yield();
    
                sum+=i;
    
            }
    
            long end=System.currentTimeMillis();
    
            System.out.println("sum =" +sum);
    
            System.out.println("sum cost time " +(end-begin)+" ms.");
    
        }
    
        public static void main(String[]args){
    
            YieldThread thread=new YieldThread();
    
            thread.start();
    
        }
    
    }
    

    运行结果:

    sum =1249999975000000
    sum cost time 47 ms.
    

    取消掉上述代码注释后运行结果:

    sum =1249999975000000
    sum cost time 10332 ms
    

    对象方法

    isAlive()方法

    isAlive()方法是判断当前线程是否处于活动状态。活动状态是线程已经启动且尚未终止。
    在JDK中实现如下:

    public final native boolean isAlive();
    

    举例说明:

    public class Run2 extends Thread {
    
        @Override
    
        public void run(){
    
            System.out.println("run = "+this.isAlive());
    
        }
    
        public static void main(String[]args) throws InterruptedException {
    
            Run2 run2=new Run2();
    
            System.out.println("begin == "+run2.isAlive());
    
            run2.start();
    
            System.out.println("end == "+run2.isAlive());
    
            Thread.sleep(1000);
    
            System.out.println("Thread end == "+run2.isAlive());
    
        }
    
    }
    

    运行结果:

    begin == false
    end == true
    
    run = true
    Thread end == false
    

    上述结果说明:在线程run2没有执行start()方法的时候,线程还未运行,isAlive()=false,在执行start()之后,这里的结果其实是不确定的,end == true说明该线程还没有运行完成,因此isAlive=true,在睡眠1s之后,Thread end == false说明线程已经运行完成,所以isAlive==false.

    join()方法

    方法名称 方法说明
    public final synchronized void join(long millis);
    public final void join();
    public final synchronized void join(long millis, int nanos);
    如果线程A执行了thread.join()语句,则当前线程A等待thread线程终止之后才从thread.join()返回。两个超时方法表示,如果线程thread在给定的超时时间里没有终止,那么将会从超时方法中返回

    join()方法的实现调用了wait()方法,当线程终止时,会调用线程自身的notifyAll()方法,会通知所有等待在该线程对象上的线程。JDK实现如下:

    ///此处A timeout of 0 means to wait forever 字面意思是永远等待,其实是等到t结束后。  
    public final synchronized void join(long millis)
    
        throws InterruptedException {
    
            long base = System.currentTimeMillis();
    
            long now = 0;
    
            if (millis < 0) {
    
                throw new IllegalArgumentException("timeout value is negative");
    
            }
    
            if (millis == 0) {
    
                while (isAlive()) {//判断线程是否处于活动状态
    
                    wait(0);//释放锁
    
                }
    
            } else {
    
                while (isAlive()) {
    
                    long delay = millis - now;
    
                    if (delay <= 0) {
    
                        break;
    
                    }
    
                    wait(delay);
    
                    now = System.currentTimeMillis() - base;
    
                }
    
            }
    
        }
    

    从源码可以看出:如果线程被生成了,但还未被起动,isAlive()将返回false,调用它的join()方法是没有作用的。将直接继续向下执行。

    当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁。

    举例说明:

    public class JoinThread {
    
        public static void main(String[]args){
    
            Thread previous=Thread.currentThread();
    
            for(int i=0;i<10;i++){
    
                Thread thread=new Thread(new Domino(previous),String.valueOf(i));
    
                thread.start();
    
                previous=thread;
    
            }
    
        }
    
         public static class Domino implements Runnable{
    
             private Thread thread;
    
             public Domino(Thread thread){
    
                 this.thread=thread;
    
             }
    
             @Override
    
             public void run() {
    
                 try{
    
                     thread.join();
    
                 } catch (InterruptedException e) {
    
                 }
    
                 System.out.println(Thread.currentThread().getName()+" terminate.");
    
             }
    
         }
    
    }
    

    运行结果:

    0 terminate.
    
    1 terminate.
    
    2 terminate.
    
    3 terminate.
    
    4 terminate.
    
    5 terminate.
    
    6 terminate.
    
    7 terminate.
    
    8 terminate.
    
    9 terminate.
    

    join(long)方法与sleep(long)方法的区别

    两个方法的区别主要是在对同步的处理上。

    方法join(long)的功能在内部是使用wait(long)方法来实现的,所以join(long)具有释放对象锁的特点。从源码中可以看出,当执行wait(long)方法后,当前线程的锁被释放,那么其他线程就可以调用此线程的同步方法了。而Thread.sleep(long)方法却不释放锁。

    停止线程

    在介绍如何安全的停止线程之前,先介绍3个不好的方法,在这里只说明不好的方法的缺点,不详细举例说明。
    stop()方法-已作废,强制停止线程

    stop()方法在终结一个线程时不会保证线程的资源正常释放,没有给予线程完成资源释放工作的机会,可能会导致程序工作在不确定的状态下。

    suspend()方法-已作废,暂停线程
    resume()方法-已作废,恢复线程

    suspend()/resume()方法在调用后,线程不会释放已经占有的资源(比如锁),而是占着资源进入睡眠状态,这样荣引起死锁问题。还容易出现因为线程的暂停而导致数据不同步的情况。

    安全的终止线程

    中断法+boolean变量法

    通过这两种方式能够使线程咋终止时有机会去清理资源,而不是武断 地将线程停止,这两种方式使得线程的终止显得更加安全和优雅。

    实例如下:

    public class Shutdown {
    
        public static void main(String[]args) throws InterruptedException {
    
    //中断法
    
            Runner1 one=new Runner1();
    
            Thread countThread=new Thread(one,"CountThread");
    
            countThread.start();
    
            TimeUnit.SECONDS.sleep(1);
    
            countThread.interrupt();
    
    ////boolean变量法
    
            Runner2 two=new Runner2();
    
            countThread=new Thread(two,"CountThread");
    
            countThread.start();
    
            TimeUnit.SECONDS.sleep(1);
    
            two.cancel();
    
        }
    
        private static class Runner1 implements Runnable{
    
            private long i;
    
            @Override
    
            public void run() {
    
                while (!Thread.currentThread().isInterrupted()){
    
                    i++;
    
                }
    
                System.out.println("count="+i);
    
            }
    
        }
    
        private static class Runner2 implements Runnable{
    
            private long i;
    
            private volatile boolean on=true;
    
            @Override
    
            public void run() {
    
                while (on){
    
                    i++;
    
                }
    
                System.out.println("count="+i);
    
            }
    
            public void cancel(){
    
                on=false;
    
            }
    
        }
    
    }
    

    运行结果:

    count=24098615
    count=511335299
    

    从上面的结果看出,run()方法里面,while循环之外,还是会继续运行,打印count还是能够打印出来。
    为了解决上述问题,还有一种方式来终止线程。

    抛出异常法+return法

    public class Shutdown2 {
    
        public static void main(String[]args) throws InterruptedException {
    
            Runner1 one=new Runner1();
    
            Thread countThread=new Thread(one,"CountThread");
    
            countThread.start();
    
            TimeUnit.SECONDS.sleep(1);
    
            countThread.interrupt();
    
        }
    
        private static class Runner1 implements Runnable{
    
            private long i=0;
    
            @Override
    
            public void run() {
    
                try {
    
                    while (!Thread.currentThread().isInterrupted()){
    
                        System.out.println("i="+(i++));
    
                    }
    
                    if(Thread.currentThread().isInterrupted()){
    
                        System.out.println("Thread end!!!");
    
                       // throw new InterruptedException();
    
                       //return;
    
                    }
    
                    System.out.println("below for");
    
                } catch (InterruptedException e) {
    
                    System.out.println("in catch");
    
                    e.printStackTrace();
    
                }
    
            }
    
        }
    
    }
    

    运行结果(注释 // throw new InterruptedException();):

    ..................
    
    i=174485
    
    i=174486
    
    i=174487
    
    i=174488
    
    i=174489
    
    i=174490
    
    Thread end!!!
    
    below for
    

    运行结果(去掉注释 // throw new InterruptedException()):

    
    i=168998
    
    i=168999
    
    i=169000
    
    i=169001
    
    Thread end!!!
    
    in catch
    
    java.lang.InterruptedException
    
        at Thread.Shutdown2$Runner1.run(Shutdown2.java:23)
    
        at java.lang.Thread.run(Thread.java:748)
    
    Process finished with exit code 0
    

    结果分析:用抛出异常的方式,没有打印出"below for".

    return法

    上述代码,注释掉// throw new InterruptedException();取消注释return;

    运行结果:

    i=164759
    
    i=164760
    
    i=164761
    
    i=164762
    
    Thread end!!!
    

    结果分析:同样没有"below for".

    线程优先级

    在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。设置线程优先级有助于帮“线程规划器”确定下一次选择哪个线程来优先执行。

    设置线程的优先级使用setPriority(int)方法,默认优先级是5.在Java中,线程的优先级分为1~10这10个等级,如果小于1或者大于10,则JDK抛出异常throw new IllegalArgumentException()。设置优先级时,针对频繁阻塞(休眠或者I/O操作)的线程需要设置较高优先级,而偏重计算(需要较多CPU时间或者偏运算)的线程则设置较低的优先级,确保处理器不会被独占。在不同的JVM以及操作系统上,线程规划会存在差异,有些操作系统甚至会忽略对线程优先级的设定。

    JDK中使用3个常量来预置定义优先级的值。如下:

          /**
    
         * The minimum priority that a thread can have.
    
         */
    
        public final static int MIN_PRIORITY = 1;
    
       /**
    
         * The default priority that is assigned to a thread.
    
         */
    
        public final static int NORM_PRIORITY = 5;
    
        /**
    
         * The maximum priority that a thread can have.
    
         */
    
        public final static int MAX_PRIORITY = 10;
    

    线程优先级的特性:

    1. 继承性
      在Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。

    2. 规则性
      线程的优先级具有一定的规则性,也就是CPU尽量将执行资源让给优先级比较高的线程。

    3. 随机性
      线程的优先级具有随机性,也就是优先级较高的线程不一定每次都先执行完。当有大量优先级高的线程和大量优先级低的线程一起执行的时候,呈现出来的是大多数的优先级高的线程先执行完,但是不是每一个高优先级的线程都比低优先级的线程先执行完。

    守护线程(Daemon线程)

    在Java线程中有两种线程,一种是用户线程,一种是守护线程。

    Daemon线程是一种支持性线程,因为他主要被用作程序中后台调度以及支持性工作。这意味着,当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。可以通过调用Thread.setDaemon(true)将线程设置为Daemon线程。Daemon属性需要在启动线程之前设置,不能在启动之后设置。

    典型的守护线程就是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。Daemon线程的作用是为其他线程的运行提供便利服务。

    Daemon线程被用作完成支持性工作,但是在Java虚拟机退出时Daemon线程中的finally块并不一定会执行,实例如下:

    public class DaemonThread extends Thread{
    
        public void run(){
    
            try {
    
                Thread.sleep(10);
    
            } catch (InterruptedException e) {
    
                e.printStackTrace();
    
            }finally {
    
                System.out.println("DaemonThread finally run.");
    
            }
    
        }
    
        public static void main(String[]args){
    
            DaemonThread thread=new DaemonThread();
    
            thread.setName("DaemonThread");
    
            thread.setDaemon(true);
    
            thread.start();
    
        }
    
    }
    

    运行结果:无
    结果说明:
    运行上述程序,可以看到在中断或者命令提示符上没有任何输出。main线程(非Daemon线程)在启动了DaemonThread之后随着main方法执行完毕而终止,而此时Java虚拟机中已经没有非Daemon线程,虚拟机需要退出。Java虚拟机中的所有Daemon线程都需要立即终止,因此DaemonThread立即终止,但是DaemonThread中的finally块并没有执行。在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。

    线程的状态(线程的生命周期)

    状态名称 说明
    NEW 初始状态,线程被构建,但是还没有调用start()方法
    RUNNABLE 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称为“运行中”
    BLOCKED 阻塞状态,表示线程阻塞于锁
    WAITING 等待状态,标识线程进入等待状态,进入该状态标识当前线程需要等待其他线程做出一些特定动作(通知或中断)
    TIME_WAITING 超时等待状态,该状态不同于WAITING,它可以在指定的时间自行返回的
    TERMINATED 终止状态,标识当前线程已经执行完毕

    Java线程在运行的生命周期中有6种状态,在给定一个时刻,线程只能处于其中的一个状态。

    状态名称 说明
    NEW 初始状态,线程被构建,但是还没有调用start()方法
    RUNNABLE 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称为“运行中”
    BLOCKED 阻塞状态,表示线程阻塞于锁
    WAITING 等待状态,标识线程进入等待状态,进入该状态标识当前线程需要等待其他线程做出一些特定动作(通知或中断)
    TIME_WAITING 超时等待状态,该状态不同于WAITING,它可以在指定的时间自行返回的
    TERMINATED 终止状态,标识当前线程已经执行完毕

    在Java中线程的状态分为6种

    1. 初始状态:new
      创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态。
    2. 运行态:runnable
      在java中,运行态包含就绪态和运行态。
      • 就绪态

        • 该状态下的线程已经获得执行所需的所有资源,只要CPU分配执行权就可以运行。
        • 所有就绪状态的线程存放在就绪队列中。
      • 运行态

        • 获得CPU执行权,正在执行的线程。
        • 由于一个CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一条运行态的线程。
    3. 阻塞态:blocked
      • 当一条正在执行的线程请求你某一资源失败时,就会进入阻塞态。
      • 在Java中,阻塞态专指请求锁失败时进入的状态。(进入synchronized关键字修饰的方法或代码块时,获取锁失败)
      • 由一个阻塞队列存放所有阻塞态的线程。
      • 处于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待 执行。
    4. 等待态:waiting
      • 当前线程调用wait/join/park函数时,当前线程就会进入等待态。
      • 也有一个等待队列存放所有等待态的线程。
      • 线程处于等待态标识它需要等待其他线程的指示才能继续运行。
      • 进入等待态的线程会释放CPU执行权,并释放资源(如锁)。
    5. 超时等待态:timed_waiting
      • 当运行中的线程调用sleep(time)/wait/join/parkNanos/parkUntil时,就会进入该状态。
      • 它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其他线程唤醒。
      • 进入该状态后会释放CPU执行权和占有的资源。
      • 与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。
    6. 终止态:terminated
      • 线程执行结束后的状态。

    阻塞在java.concurrent包中的Lock接口的线程是等待状态,因为java.concurrent包中的Lock接口对于阻塞的实现均使用了LockSupport类中的相关方法。

    Java线程各状态之间的转换如下图所示:


    Image.png

    参考:
    Java多线程编程核心技术
    Java并发编程的的艺术

    该文是我的读书笔记,对知识进行总结,帮助自己以后回顾。

    相关文章

      网友评论

          本文标题:多线程系列(一)--线程基础

          本文链接:https://www.haomeiwen.com/subject/uvewnxtx.html