精华内容
下载资源
问答
  • 线程的状态

    千次阅读 2018-11-15 14:47:28
    Java中线程的状态 在Thread类中有一个枚举类型,其中定义了线程的不同状态。分别是NEW(新生状态)、RUNNABLE(可运行状态)、BLOCKED(被阻塞状态)、WAITING(等待状态)、TIMED_WAITING(计时等待)和TERMIN...

    线程具有生命周期,线程的生命周期中包含不同的状态,在操作系统中,线程至少有五个基本状态,分别是:新生状态、就绪状态、运行状态、阻塞状态和死亡状态。
    在这里插入图片描述在这里插入图片描述

    Java中线程的状态

    在Thread类中有一个枚举类型,其中定义了线程的不同状态。分别是NEW(新生状态)、RUNNABLE(可运行状态)、BLOCKED(被阻塞状态)、WAITING(等待状态)、TIMED_WAITING(计时等待)和TERMINATED(被终止状态)。
    在这里插入图片描述
    注意:在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。

    新生状态

    当用new操作符创建一个新线程时,如new Thread(thread),该线程还没有开启(即还没有调用start()方法),这就意味着它的状态是新生状态了。当一个线程处于新生状态时,程序还没有开始运行线程中的代码。

    可运行状态

    一旦线程调用start方法,线程就可以被调度运行,处于RUNNABLE状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统的调度。

    注意:Java规范中没有将正在运行中的线程作为一个单独的状态,它任然处于可运行状态。

    一旦一个线程开始运行,它不必始终保持运行,也就是说它的运行可以被中断。事实上,运行中的线程被中断,目的是为了让其他线程获得运行的机会。

    线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行线程一个时间片来执行任务。当时间片用完,操作系统将剥夺该线程的运行权,并给另外一个线程运行机会。当选择下一个线程时,操作系统将考虑线程的优先级。

    被阻塞状态和等待状态

    当线程处于被阻塞或等待状态时,它暂时不活动。它不运行任何代码且消耗最少的资源。直到线程调度器重新激活它。

    阻塞状态(BLOCKED):当一个线程试图获取一个内部的对象锁(不是java.util.concurrent库中的锁),而该锁被其他线程持有,则该线程进入阻塞状态。当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程将处于非阻塞状态。

    等待状态(WAITING):当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。在调用Object.wait方法或者Thread.join方法,或者是等待java.util.concurrent库中的Lock或Condition时,就会出现这种情况。

    计时等待状态(TIMED_WAITING):有几个方法有一个超时参数,调用它们将导致线程进入计时等待状态。这以状态将一直保持到超时期满或者接收到适当的通知。带有超时参数的方法有Thread.sleep和Object.wait、Thread.join等。

    被终止状态

    线程被终止一般有两个原因:一是run()方法正常执行完毕而自然死亡;二是因为一个没有捕获的异常终止了run方法而异外死亡。

    被终止了的线程不能再回到其他状态,更不能再次被运行。

    展开全文
  • 线程的状态和基本操作

    千次阅读 多人点赞 2019-10-03 23:36:42
    在上一篇博客中并发编程的优...文章目录创建线程的四种方式线程的状态和生命周期线程状态的基本操作interruptedjoinsleepyield进程和线程线程优先级守护线程和用户线程守护线程详解线程死锁认识线程死锁如何避免线...

    在上一篇博客中并发编程的优缺点谈到了为什么花功夫去学习并发编程的技术。万事开头难,接下来就应该了解如何新建一个线程?线程状态是怎样转换的?关于线程状态的操作是怎样的?这篇博客就主要围绕这三个方面来聊一聊。

    创建线程的四种方式

    创建线程的四种方式

    1. 继承Thread类
    2. 实现Runnable接口
    3. 使用Callable和Future创建线程
    4. 使用Executor框架创建线程池

    创建线程的具体实现可以参考创建线程的四种方式

    线程的状态和生命周期

    Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态(图源《Java 并发编程艺术》4.1.4 节)。

    Java线程的状态

    线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程状态变迁如下图所示(图源《Java 并发编程艺术》4.1.4 节):

    在这里插入图片描述

    由上图可以看出:线程创建之后它将处于 NEW(新建) 状态,调用 start() 方法后开始运行,线程这时候处于 READY(可运行) 状态。可运行状态的线程获得了 CPU 时间片(timeslice)后就处于 RUNNING(运行) 状态。

    操作系统隐藏 Java 虚拟机(JVM)中的 RUNNABLE 和 RUNNING 状态,它只能看到 RUNNABLE 状态,所以 Java 系统一般将这两个状态统称为 RUNNABLE(运行中) 状态 。

    在这里插入图片描述

    当线程执行 wait()方法之后,线程进入 **WAITING(等待)**状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 TIME_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,比如通过 sleep(long millis)方法或 wait(long millis)方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnable 的run()方法之后将会进入到 TERMINATED(终止) 状态。

    线程状态的基本操作

    除了新建一个线程外,线程在生命周期内还有需要进行一些基本操作,而这些操作会成为线程间一种通信方式,比如使用中断(interrupted)方式通知实现线程间的交互等等,下面就将具体说说这些操作。

    interrupted

    中断可以理解为线程的一个标志位,它表示了一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了一个招呼。其他线程可以调用该线程的interrupt()方法对其进行中断操作,同时该线程可以调用
    isInterrupted()来感知其他线程对其自身的中断操作,从而做出响应。另外,同样可以调用Thread的静态方法
    interrupted()对当前线程进行中断操作,该方法会清除中断标志位。需要注意的是,当抛出InterruptedException时候,会清除中断标志位,也就是说在调用isInterrupted会返回false。

    方法名 详细解释 备注
    public void interrupt() 中断该线程对象 如果该线程被调用了Object wait/Object wait(long),或者被调用sleep(long),join()/join(long)方法时会抛出interruptedException并且中断标志位将会被清除
    public boolean isinterrupted() 测试该线程对象是否被中断 中断标志位不会被清除
    public static boolean interrupted() 测试当前线程是否被中断 中断标志位会被清除

    下面结合具体的实例来看一看

    public class InterruptDemo {
        public static void main(String[] args) throws InterruptedException {
            //sleepThread睡眠1000ms
            final Thread sleepThread = new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    super.run();
                }
            };
            //busyThread一直执行死循环
            Thread busyThread = new Thread() {
                @Override
                public void run() {
                    while (true) ;
                }
            };
            sleepThread.start();
            busyThread.start();
            sleepThread.interrupt();
            busyThread.interrupt();
            while (sleepThread.isInterrupted()) ;
            System.out.println("sleepThread isInterrupted: " + sleepThread.isInterrupted());
            System.out.println("busyThread isInterrupted: " + busyThread.isInterrupted());
        }
    }
    

    输出结果

    sleepThread isInterrupted: false
    busyThread isInterrupted: true
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at com.jourwon.test.InterruptDemo$1.run(InterruptDemo.java:17)
    

    开启了两个线程分别为sleepThread和BusyThread, sleepThread睡眠1s,BusyThread执行死循环。然后分别对着两个线程进行中断操作,可以看出sleepThread抛出InterruptedException后清除标志位,而busyThread就不会清除标志位。

    另外,同样可以通过中断的方式实现线程间的简单交互, while (sleepThread.isInterrupted()) 表示在Main线程中会持续监测sleepThread线程,一旦sleepThread的中断标志位清零,即sleepThread.isInterrupted()返回为false时才会继续Main线程才会继续往下执行。因此,中断操作可以看做线程间一种简便的交互方式。一般在结束线程时通过中断标志位或者标志位的方式可以有机会去清理资源,相对于武断而直接的结束线程,这种方式要优雅和安全

    join

    join方法可以看做是线程间协作的一种方式,很多时候,一个线程的输入可能非常依赖于另一个线程的输出,这就像两个好基友,一个基友先走在前面突然看见另一个基友落在后面了,这个时候他就会在原处等一等这个基友,等基友赶上来后,就两人携手并进。其实线程间的这种协作方式也符合现实生活。在软件开发的过程中,从客户那里获取需求后,需要经过需求分析师进行需求分解后,这个时候产品,开发才会继续跟进。如果一个线程实例A执行了threadB.join(),其含义是:当前线程A会等待threadB线程终止后threadA才会继续执行。关于join方法一共提供如下这些方法:

    方法名 详细注释 备注
    public final void join() throws InterruptedException 等待这个线程死亡。 如果任何线程中断当前线程,如果抛出InterruptedException异常时,当前线程的中断状态将被清除
    public final void join(long millis) throws InterruptedException 等待这个线程死亡的时间最多为millis毫秒。 0的超时意味着永远等待。 如果millis为负数,抛出IllegalArgumentException异常
    public final void join(long millis, int nanos) throws InterruptedException 等待最多millis毫秒加上这个线程死亡的nanos纳秒。 如果millis为负数或者nanos不在0-999999范围抛出IllegalArgumentException异常

    Thread类除了提供join()方法外,另外还提供了超时等待的方法,如果线程threadB在等待的时间内还没有结束的话,threadA会在超时之后继续执行。join方法源码关键是:

     while (isAlive()) {
        wait(0);
     }
    

    可以看出来当前等待对象threadA会一直阻塞,直到被等待对象threadB结束后即isAlive()返回false的时候才会结束while循环,当threadB退出时会调用notifyAll()方法通知所有的等待线程。下面用一个具体的例子来说说join方法的使用:

    public class JoinDemo {
        public static void main(String[] args) {
            Thread previousThread = Thread.currentThread();
            for (int i = 1; i <= 10; i++) {
                Thread curThread = new JoinThread(previousThread);
                curThread.start();
                previousThread = curThread;
            }
        }
    
        static class JoinThread extends Thread {
            private Thread thread;
    
            public JoinThread(Thread thread) {
                this.thread = thread;
            }
    
            @Override
            public void run() {
                try {
                    thread.join();
                    System.out.println(thread.getName() + " terminated.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    输出结果为:

    main terminated.
    Thread-0 terminated.
    Thread-1 terminated.
    Thread-2 terminated.
    Thread-3 terminated.
    Thread-4 terminated.
    Thread-5 terminated.
    Thread-6 terminated.
    Thread-7 terminated.
    Thread-8 terminated.
    

    在上面的例子中一个创建了10个线程,每个线程都会等待前一个线程结束才会继续运行。可以通俗的理解成接力,前一个线程将接力棒传给下一个线程,然后又传给下一个线程…

    sleep

    public static native void sleep(long millis)方法显然是Thread的静态方法,很显然它是让当前线程按照指定的时间休眠,其休眠时间的精度取决于处理器的计时器和调度器。需要注意的是如果当前线程获得了锁,sleep方法并不会失去锁。sleep方法经常拿来与Object.wait()方法进行比价,这也是面试经常被问的地方。

    sleep() VS wait()

    两者主要的区别:

    1. sleep()方法是Thread的静态方法,而wait是Object实例方法
    2. wait()方法必须要在同步方法或者同步块中调用,也就是必须已经获得对象锁。而sleep()方法没有这个限制可以在任何地方使用。另外,wait()方法会释放占有的对象锁,使得该线程进入等待池中,等待下一次获取资源。而sleep()方法只是会让出CPU并不会释放掉对象锁;
    3. sleep()方法在休眠时间达到后,如果再次获得CPU时间片就会继续执行,而wait()方法必须等待Object.notift/Object.notifyAll通知后,才会离开等待池,并且再次获得CPU时间片才会继续执行。

    yield

    public static native void yield()这是一个静态方法,一旦执行,它会是当前线程让出CPU,但是,需要注意的是,让出的CPU并不是代表当前线程不再运行了,如果在下一次竞争中,又获得了CPU时间片当前线程依然会继续运行。另外,让出的时间片只会分配给当前线程相同优先级的线程。什么是线程优先级了?下面就来具体聊一聊。

    现代操作系统基本采用时分的形式调度运行的线程,操作系统会分出一个个时间片,线程会分配到若干时间片,当前时间片用完后就会发生线程调度,并等待下次分配。线程分配到的时间多少也就决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要或多或少分配一些处理器资源的线程属性。

    在Java程序中,通过一个整型成员变量Priority来控制优先级,优先级的范围从1~10.在构建线程的时候可以通过**setPriority(int)**方法进行设置,默认优先级为5,优先级高的线程相较于优先级低的线程优先获得处理器时间片。需要注意的是在不同JVM以及操作系统上,线程规划存在差异,有些操作系统甚至会忽略线程优先级的设定。

    另外需要注意的是,sleep()和yield()方法,同样都是当前线程会交出处理器资源,而它们不同的是,sleep()交出来的时间片其他线程都可以去竞争,也就是说都有机会获得当前线程让出的时间片。而yield()方法只允许与当前线程具有相同优先级的线程能够获得释放出来的CPU时间片。

    进程和线程

    进程和线程的详细区别请参考进程和线程的区别(超详细)

    线程优先级

    理论上来说系统会根据优先级来决定首先使哪个线程进入运行状态。当 CPU 比较闲的时候,设置线程优先级几乎不会有任何作用,而且很多操作系统压根不会理会你设置的线程优先级,所以不要让业务过度依赖于线程的优先级。

    另外,线程优先级具有继承特性比如 A 线程启动 B 线程,则 B 线程的优先级和 A 是一样的。线程优先级还具有随机性 也就是说线程优先级高的不一定每一次都先执行完。

    Thread 类中包含的成员变量代表了线程的某些优先级。如Thread.MIN_PRIORITY(常数 1)Thread.NORM_PRIORITY(常数 5),Thread.MAX_PRIORITY(常数 10)。其中每个线程的优先级都在110 之间,1的优先级为最低,10的优先级为最高,在默认情况下优先级都是Thread.NORM_PRIORITY(常数 5)

    一般情况下,不会对线程设定优先级别,更不会让某些业务严重地依赖线程的优先级别,比如权重,借助优先级设定某个任务的权重,这种方式是不可取的,一般定义线程的时候使用默认的优先级就好了。

    相关方法:

    public final void setPriority(int newPriority) //为线程设定优先级
    public final int getPriority() //获取线程的优先级
    

    设置线程优先级方法源码:

    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        //线程游戏优先级不能小于 1 也不能大于 10,否则会抛出异常
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        //如果指定的线程优先级大于该线程所在线程组的最大优先级,那么该线程的优先级将设为线程组的最大优先级
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }
    

    守护线程和用户线程

    守护线程和用户线程简介:

    • 用户 (User) 线程:运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
    • 守护 (Daemon) 线程:运行在后台,为其他前台线程服务。也可以说守护线程是 JVM 中非守护线程的 “佣人”。一旦所有用户线程都结束运行,守护线程会随 JVM 一起结束工作

    main 函数所在的线程就是一个用户线程啊,main 函数启动的同时在 JVM 内部同时还启动了好多守护线程,比如垃圾回收线程。

    那么守护线程和用户线程有什么区别呢?

    比较明显的区别之一是用户线程结束,JVM 退出,不管这个时候有没有守护线程运行。而守护线程不会影响 JVM 的退出。

    注意事项:

    1. setDaemon(true)必须在start()方法前执行,否则会抛出 IllegalThreadStateException 异常
    2. 在守护线程中产生的新线程也是守护线程
    3. 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑
    4. 守护 (Daemon) 线程中不能依靠 finally 块的内容来确保执行关闭或清理资源的逻辑。因为我们上面也说过了一旦所有用户线程都结束运行,守护线程会随 JVM 一起结束工作,所以守护 (Daemon) 线程中的 finally 语句块可能无法被执行。

    守护线程详解

    守护线程是一种特殊的线程,就和它的名字一样,它是系统的守护者,在后台默默地守护一些系统服务,比如垃圾回收线程,JIT线程就可以理解为守护线程。与之对应的就是用户线程,用户线程就可以认为是系统的工作线程,它会完成整个系统的业务操作。用户线程完全结束后就意味着整个系统的业务任务全部结束了,因此系统就没有对象需要守护的了,守护线程自然而然就会退。当一个Java应用,只有守护线程的时候,虚拟机就会自然退出。下面以一个简单的例子来表述Daemon线程的使用。

    public class DaemonDemo {
        public static void main(String[] args) {
            Thread daemonThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            System.out.println("i am alive");
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            System.out.println("finally block");
                        }
                    }
                }
            });
            daemonThread.setDaemon(true);
            daemonThread.start();
            //确保main线程结束前能给daemonThread能够分到时间片
            try {
                Thread.sleep(800);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    输出结果为:

    i am alive
    finally block
    i am alive
    

    上面的例子中daemodThread run()方法中是一个while死循环,会一直打印,但是当main线程结束后daemonThread就会退出所以不会出现死循环的情况。main线程先睡眠800ms保证daemonThread能够拥有一次时间片的机会,也就是说可以正常执行一次打印“i am alive”操作和一次finally块中"finally block"操作。紧接着main 线程结束后,daemonThread退出,这个时候只打印了"i am alive"并没有打印finnal块中的。因此,这里需要注意的是守护线程在退出的时候并不会执行finnaly块中的代码,所以守护 (Daemon) 线程中不能依靠 finally 块的内容来确保执行关闭或清理资源的逻辑

    线程可以通过setDaemon(true)的方法将线程设置为守护线程。并且需要注意的是设置守护线程要先于start()方法,否则会报

    Exception in thread "main" java.lang.IllegalThreadStateException
    

    但是该线程还是会执行,只不过会当做正常的用户线程执行。

    线程死锁

    认识线程死锁

    百度百科:死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。

    多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

    如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

    线程死锁

    下面通过一个例子来说明线程死锁,代码模拟了上图的死锁的情况 (代码来源于《并发编程之美》):

    public class DeadLockDemo {
        private static Object resource1 = new Object();//资源 1
        private static Object resource2 = new Object();//资源 2
    
        public static void main(String[] args) {
            new Thread(() -> {
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource2");
                    synchronized (resource2) {
                        System.out.println(Thread.currentThread() + "get resource2");
                    }
                }
            }, "线程 1").start();
    
            new Thread(() -> {
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + "get resource2");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource1");
                    synchronized (resource1) {
                        System.out.println(Thread.currentThread() + "get resource1");
                    }
                }
            }, "线程 2").start();
        }
    }
    

    输出结果

    Thread[线程 1,5,main]get resource1
    Thread[线程 2,5,main]get resource2
    Thread[线程 1,5,main]waiting get resource2
    Thread[线程 2,5,main]waiting get resource1
    

    线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过Thread.sleep(1000);让线程 A 休眠 1s 为的是让线程 B 得到CPU执行权,然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。上面的例子符合产生死锁的四个必要条件。

    形成死锁的四个必要条件:

    1. 互斥条件:线程(进程)对于所分配到的资源具有排它性,即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放
    2. 请求与保持条件:一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
    3. 不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
    4. 循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞

    如何避免线程死锁

    我们只要破坏产生死锁的四个条件中的其中一个就可以了。

    破坏互斥条件

    这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。

    破坏请求与保持条件

    一次性申请所有的资源。

    破坏不剥夺条件

    占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

    破坏循环等待条件

    靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

    我们对线程 2 的代码修改成下面这样就不会产生死锁了。

    new Thread(() -> {
        synchronized (resource1) {
            System.out.println(Thread.currentThread() + "get resource1");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread() + "waiting get resource2");
            synchronized (resource2) {
                System.out.println(Thread.currentThread() + "get resource2");
            }
        }
    }, "线程 2").start();
    

    输出结果

    Thread[线程 1,5,main]get resource1
    Thread[线程 1,5,main]waiting get resource2
    Thread[线程 1,5,main]get resource2
    Thread[线程 2,5,main]get resource1
    Thread[线程 2,5,main]waiting get resource2
    Thread[线程 2,5,main]get resource2
    

    我们分析一下上面的代码为什么避免了死锁的发生?

    线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了破坏循环等待条件,因此避免了死锁。

    展开全文
  • 线程的状态转换

    千次阅读 2015-05-26 19:12:40
    线程的状态有:new、runnable、running、waiting、timed_waiting、blocked、dead  当执行new Thread(Runnabler)后,新创建出来的线程处于new状态,这种线程不可能执行  当执行thread.start()后,线程处于...
    线程的状态有:new、runnable、running、waiting、timed_waiting、blocked、dead 

    当执行new Thread(Runnabler)后,新创建出来的线程处于new状态,这种线程不可能执行 

    当执行thread.start()后,线程处于runnable状态,这种情况下只要得到CPU,就可以开始执行了。runnable状态的线程,会接受JVM的调度,进入running状态,但是具体何时会进入这个状态,是随机不可知的 

    running状态中的线程最为复杂,可能会进入runnable、waiting、timed_waiting、blocked、dead状态: 
    如果CPU调度给了别的线程,或者执行了Thread.yield()方法,则进入runnable状态,但是也有可能立刻又进入running状态 
    如果执行了Thread.sleep(long),或者thread.join(long),或者在锁对象上调用object.wait(long)方法,则会进入timed_waiting状态 
    如果执行了thread.join(),或者在锁对象上调用了object.wait()方法,则会进入waiting状态 
    如果进入了同步方法或者同步代码块,没有获取锁对象的话,则会进入blocked状态 

    处于waiting状态中的线程,如果是因为thread.join()方法进入等待的话,在目标thread执行完毕之后,会回到runnable状态;如果是因为object.wait()方法进入等待的话,在锁对象执行object.notify()或者object.notifyAll()之后会回到runnable状态 

    处于timed_waiting状态中的线程,和waiting状态中的差不多,只不过是设定时间到了,就会回到runnable状态 

    处于blocked状态中的线程,只有获取了锁之后,才会脱离阻塞状态 

    当线程执行完毕,或者抛出了未捕获的异常之后,会进入dead状态,该线程结束



    参考的资源:

    http://go-on.iteye.com/blog/1673894

    http://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html

    展开全文
  • 本章主要对Java中线程的状态转换进行学习。 1.前言 在之前的章节中,已经学习了java的各种方法,涉及到状态转换的方法有: Thread.sleep(long):强制线程睡眠一段时间。 thread.start():启动一个线程。 thread....

    [超级链接:Java并发学习系列-绪论]

    本章主要对Java中线程的状态转换进行学习。

    1.前言

    在之前的章节中,已经学习了java的各种方法,涉及到状态转换的方法有:

    • Thread.sleep(long):强制线程睡眠一段时间。
    • thread.start():启动一个线程。
    • thread.join():在当前线程中加入指定线程,使得这个指定线程等待当前线程,并在当前线程结束前结束。
    • thread.yield():使得当前线程退让出CPU资源,把CPU调度机会分配给同样线程优先级的线程。
    • thread.interrupt():使得指定线程中断阻塞状态,并将阻塞标志位置为true。
    • object.wai()、object.notify()、object.notifyAll():Object类提供的线程等待和线程唤醒方法。

    本章主要就线程的状态即通过上述方法产生的状态转换情况进行学习。

    2.Java线程的状态

    关于Java线程的状态,都规定在了Thread.State这个枚举类型中,我们先看看Thread.State的源码:

    public enum State {
       /**
         * Thread state for a thread which has not yet started.
         */
        NEW,
    
        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,
    
        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,
    
        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,
    
        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,
    
        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

    从源代码,总结出Java线程的状态有以下几种:

    • NEW:一个尚未启动的线程的状态。也称之为初始状态、开始状态
    • RUNNABLE:一个可以运行的线程的状态,可以运行是指这个线程已经在JVM中运行了,但是有可能正在等待其他的系统资源。也称之为就绪状态、可运行状态
    • BLOCKED:一个线程因为等待监视锁而被阻塞的状态。也称之为阻塞状态
    • WAITING:一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有三种,分别是调用Object.wait()、join()以及LockSupport.park()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。
    • TIMED_WAITING:一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有五种,分别是:Thread.sleep(long)、Object.wait(long)、join(long)、LockSupport.parkNanos(obj,long)和LockSupport.parkUntil(obj,long)。
    • TERMINATED:一个完全运行完成的线程的状态。也称之为终止状态、结束状态

    3.Java线程的状态转换图

    根据Thread.State的注释,可以总结成如下的线程状态流程图:
    这里写图片描述

    有些文章中的流程图与我这个流程图有很大区别,他们的流程可能包括:New、Runnable、Running、Blocked。本人也不能肯定谁对谁错,我只是按照源代码即注释进行学习。如果有不对的地方,请多多指教。

    4.实例代码及运行结果

    为了验证上面论述的状态即状态转换的正确性,也为了加深对线程状态转换的理解,下面通过四个实例进行练习。

    4.1.列出Thread.State的枚举值

    这个简单,直接上代码:

    //线程的六种状态
    LOGGER.info("======线程的六种状态======");
    LOGGER.info("线程-初始状态:" + Thread.State.NEW);
    LOGGER.info("线程-就绪状态:" + Thread.State.RUNNABLE);
    LOGGER.info("线程-阻塞状态:" + Thread.State.BLOCKED);
    LOGGER.info("线程-等待状态:" + Thread.State.WAITING);
    LOGGER.info("线程-限时等待状态:" + Thread.State.TIMED_WAITING);
    LOGGER.info("线程-终止状态:" + Thread.State.TERMINATED + "\n");

    运行结果:

    2018-03-12 21:00:28 INFO  ThreadStateDemo:22 - ======线程的六种状态======
    2018-03-12 21:00:28 INFO  ThreadStateDemo:23 - 线程-初始状态:NEW
    2018-03-12 21:00:28 INFO  ThreadStateDemo:24 - 线程-就绪状态:RUNNABLE
    2018-03-12 21:00:28 INFO  ThreadStateDemo:25 - 线程-阻塞状态:BLOCKED
    2018-03-12 21:00:28 INFO  ThreadStateDemo:26 - 线程-等待状态:WAITING
    2018-03-12 21:00:28 INFO  ThreadStateDemo:27 - 线程-限时等待状态:TIMED_WAITING
    2018-03-12 21:00:28 INFO  ThreadStateDemo:28 - 线程-终止状态:TERMINATED

    上面的运行结果,证明线程的状态确实是前面论述的六种。

    4.2.TIME_WAITING的状态转换

    需求:

    编写一段代码,依次显示一个线程的这些状态:NEW->RUNNABLE->TIME_WAITING->RUNNABLE->TERMINATED。

    分析:

    根据之前章节的学习,很容分析得到:

    • NEW:一个线程新new出来,但是还未start()的状态。
    • RUNNABLE:一个线程调用了start()之后的状态。
    • TIME_WAITING:一个线程通过Thread.sleep(long)进入限时休眠的状态。
    • RUNNABLE:Thread.sleep(long)的时间片用完,线程解除限时休眠的状态。
    • TERMINATED:一个线程运行完run()方法的状态。

    代码:

    //线程状态间的状态转换:NEW->RUNNABLE->TIME_WAITING->RUNNABLE->TERMINATED
    LOGGER.info("======线程状态间的状态转换NEW->RUNNABLE->TIME_WAITING->RUNNABLE->TERMINATED======");
    //定义一个内部线程
    Thread thread = new Thread(() -> {
        LOGGER.info("2.执行thread.start()之后,线程的状态:" + Thread.currentThread().getState());
        try {
            //休眠100毫秒
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LOGGER.info("4.执行Thread.sleep(long)完成之后,线程的状态:" + Thread.currentThread().getState());
    });
    //获取start()之前的状态
    LOGGER.info("1.通过new初始化一个线程,但是还没有start()之前,线程的状态:" + thread.getState());
    //启动线程
    thread.start();
    //休眠50毫秒
    Thread.sleep(50);
    //因为thread1需要休眠100毫秒,所以在第50毫秒,thread1处于sleep状态
    LOGGER.info("3.执行Thread.sleep(long)时,线程的状态:" + thread.getState());
    //thread1和main线程主动休眠150毫秒,所以在第150毫秒,thread1早已执行完毕
    Thread.sleep(100);
    LOGGER.info("5.线程执行完毕之后,线程的状态:" + thread.getState() + "\n");

    运行结果:

    2018-03-12 21:00:28 INFO  ThreadStateDemo:32 - ======线程状态间的状态转换NEW->RUNNABLE->TIME_WAITING->RUNNABLE->TERMINATED======
    2018-03-12 21:00:29 INFO  ThreadStateDemo:45 - 1.通过new初始化一个线程,但是还没有start()之前,线程的状态:NEW
    2018-03-12 21:00:29 INFO  ThreadStateDemo:35 - 2.执行thread.start()之后,线程的状态:RUNNABLE
    2018-03-12 21:00:29 INFO  ThreadStateDemo:51 - 3.执行Thread.sleep(long)时,线程的状态:TIMED_WAITING
    2018-03-12 21:00:29 INFO  ThreadStateDemo:42 - 4.执行Thread.sleep(long)完成之后,线程的状态:RUNNABLE
    2018-03-12 21:00:29 INFO  ThreadStateDemo:54 - 5.线程执行完毕之后,线程的状态:TERMINATED

    4.3.WAITING的状态转换

    需求:

    编写一段代码,依次显示一个线程的这些状态:NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED。

    分析:

    根据之前章节的学习,很容分析得到:

    • NEW:一个线程新new出来,但是还未start()的状态。
    • RUNNABLE:一个线程调用了start()之后的状态。
    • WAITING:一个线程通过object.wait()进入等待的状态。
    • RUNNABLE:另一个线程通过object.notify()唤醒了开始的线程,第一个线程解除等待的状态。
    • TERMINATED:一个线程运行完run()方法的状态。

    代码:

    //线程状态间的状态转换:NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED
    LOGGER.info("======线程状态间的状态转换NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED======");
    //定义一个对象,用来加锁和解锁
    AtomicBoolean obj = new AtomicBoolean(false);
    //定义一个内部线程
    Thread thread1 = new Thread(() -> {
        LOGGER.info("2.执行thread.start()之后,线程的状态:" + Thread.currentThread().getState());
        synchronized (obj) {
            try {
                //thread1需要休眠100毫秒
                Thread.sleep(100);
                //thread1100毫秒之后,通过wait()方法释放obj对象是锁
                obj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        LOGGER.info("4.被object.notify()方法唤醒之后,线程的状态:" + Thread.currentThread().getState());
    });
    //获取start()之前的状态
    LOGGER.info("1.通过new初始化一个线程,但是还没有start()之前,线程的状态:" + thread1.getState());
    //启动线程
    thread1.start();
    //main线程休眠150毫秒
    Thread.sleep(150);
    //因为thread1在第100毫秒进入wait等待状态,所以第150秒肯定可以获取其状态
    LOGGER.info("3.执行object.wait()时,线程的状态:" + thread1.getState());
    //声明另一个线程进行解锁
    new Thread(() -> {
        synchronized (obj) {
            //唤醒等待的线程
            obj.notify();
        }
    }).start();
    //main线程休眠10毫秒等待thread1线程能够苏醒
    Thread.sleep(10);
    //获取thread1运行结束之后的状态
    LOGGER.info("5.线程执行完毕之后,线程的状态:" + thread1.getState() + "\n");

    运行结果:

    2018-03-12 21:00:29 INFO  ThreadStateDemo:59 - ======线程状态间的状态转换NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED======
    2018-03-12 21:00:29 INFO  ThreadStateDemo:78 - 1.通过new初始化一个线程,但是还没有start()之前,线程的状态:NEW
    2018-03-12 21:00:29 INFO  ThreadStateDemo:64 - 2.执行thread.start()之后,线程的状态:RUNNABLE
    2018-03-12 21:00:29 INFO  ThreadStateDemo:84 - 3.执行object.wait()时,线程的状态:WAITING
    2018-03-12 21:00:29 INFO  ThreadStateDemo:75 - 4.被object.notify()方法唤醒之后,线程的状态:RUNNABLE
    2018-03-12 21:00:29 INFO  ThreadStateDemo:95 - 5.线程执行完毕之后,线程的状态:TERMINATED

    4.4.BLOCKED的状态转换

    需求:

    编写一段代码,依次显示一个线程的这些状态:NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED。

    分析:

    根据之前章节的学习,很容分析得到:

    • NEW:一个线程新new出来,但是还未start()的状态。
    • RUNNABLE:一个线程调用了start()之后的状态。
    • BLOCKED:一个线程因为等待object上的对象锁,而进入阻塞的状态。
    • RUNNABLE:另一个对象释放了object上的对象所,第一个线程解除阻塞的状态。
    • TERMINATED:一个线程运行完run()方法的状态。

    代码:

    //线程状态间的状态转换:NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED
    LOGGER.info("======线程状态间的状态转换NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED======");
    //定义一个对象,用来加锁和解锁
    AtomicBoolean obj2 = new AtomicBoolean(false);
    //定义一个线程,先抢占了obj2对象的锁
    new Thread(() -> {
        synchronized (obj2) {
            try {
                //第一个线程要持有锁100毫秒
                Thread.sleep(100);
                //然后通过wait()方法进行等待状态,并释放obj2的对象锁
                obj2.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
    //定义目标线程,获取等待获取obj2的锁
    Thread thread3 = new Thread(() -> {
        LOGGER.info("2.执行thread.start()之后,线程的状态:" + Thread.currentThread().getState());
        synchronized (obj2) {
            try {
                //thread3要持有对象锁100毫秒
                Thread.sleep(100);
                //然后通过notify()方法唤醒所有在ojb2上等待的线程继续执行后续操作
                obj2.notify();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        LOGGER.info("4.阻塞结束后,线程的状态:" + Thread.currentThread().getState());
    });
    //获取start()之前的状态
    LOGGER.info("1.通过new初始化一个线程,但是还没有thread.start()之前,线程的状态:" + thread3.getState());
    //启动线程
    thread3.start();
    //先等100毫秒
    Thread.sleep(50);
    //第一个线程释放锁至少需要100毫秒,所以在第50毫秒时,thread3正在因等待obj的对象锁而阻塞
    LOGGER.info("3.因为等待锁而阻塞时,线程的状态:" + thread3.getState());
    //再等300毫秒
    Thread.sleep(300);
    //两个线程的执行时间加上之前等待的50毫秒以供250毫秒,所以第300毫秒,所有的线程都已经执行完毕
    LOGGER.info("5.线程执行完毕之后,线程的状态:" + thread3.getState());

    运行结果:

    2018-03-12 21:00:29 INFO  ThreadStateDemo:100 - ======线程状态间的状态转换NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED======
    2018-03-12 21:00:29 INFO  ThreadStateDemo:132 - 1.通过new初始化一个线程,但是还没有thread.start()之前,线程的状态:NEW
    2018-03-12 21:00:29 INFO  ThreadStateDemo:118 - 2.执行thread.start()之后,线程的状态:RUNNABLE
    2018-03-12 21:00:29 INFO  ThreadStateDemo:138 - 3.因为等待锁而阻塞时,线程的状态:BLOCKED
    2018-03-12 21:00:29 INFO  ThreadStateDemo:129 - 4.阻塞结束后,线程的状态:RUNNABLE
    2018-03-12 21:00:29 INFO  ThreadStateDemo:142 - 5.线程执行完毕之后,线程的状态:TERMINATED

    5.结果分析

    通过分析四个代码实例,证明,开始章节说所述的线程状态以及线程状态转换都是正确的。

    展开全文
  • android 线程的状态

    千次阅读 2017-02-22 22:24:08
    1.1线程的状态 线程在它的生命周期中可能处于以下几种状态之一: · New(新生):线程对象刚刚被创建出来; · Runnable(可运行):在线程对象上调用start方法后,相应线程便会进入Runnable状态,若被线程...
  • java中线程的状态以及线程栈分析

    千次阅读 2017-03-09 18:48:10
    java中线程的状态 状态 说明 NEW 初始状态。线程刚刚被创建,并且start()方法还未被调用 RUNNABLE 运行状态。表示线程正在java虚拟机中执行,但是可能正在等待操作系统的其他...
  • 线程的状态与调度

    万次阅读 2021-03-10 20:59:49
    当我们使用new关键字新建一个线程,这个时候线程就进入了新建状态(New),也就是图中未启动状态; 调用start方法启动线程,这个时候就进入了可运行状态,也就是就绪状态(Runnable); 就绪状态获取了CPU资源,开始...
  • 线程的状态及线程结束时候资源的回收     一句话总结:线程创建的时候默认处于joinable状态,此状态线程结束的时候不会自动回收线程资源,需要pthread_join...
  • 线程的状态切换

    千次阅读 2021-02-11 20:11:35
    线程状态切换 线程从创建并启动到消亡共经历了5种状态:新建、就绪、运行、阻塞和死亡 线程变化5状态转换: 1、新建状态(New):新创建了一个线程对象。...3、执行状态(Running):就绪状态的线程获取了CP
  • 首先从一张图片来直观的了解一下线程的状态,图片来源于网络。 然后再看一下Thread.State这个枚举类,定义了线程的六种状态。 public enum State { /** * 处于NEW状态的线程此时尚未启动。 * 指的是线程建好了...
  • Java线程的状态

    万次阅读 2019-09-20 15:59:30
    * 至今尚未启动的线程处于这种状态. */ NEW, /** * 正在Java虚拟机中执行的线程处于这种状态. */ RUNNABLE, /** * 受阻塞并等待某个监视器锁的线程处于这种状态. */ BLOCKED, /** * 无限期...
  • 线程的状态 1. NEW(图中初始状态):一个刚创建而未启动的线程处于该状态。由于一个线程实例只能被启动一次,因此一个线程只可能有一次处于该状态。 2. 可运行(RUNNABLE):表示处于改状态的线程可以被JVM的线程...
  • 如何获得线程的状态的方法

    千次阅读 2014-03-18 14:15:51
    如何获得线程的状态的方法 不管是Windows API还是MFC的CWinThread类都没有给出直接获得线程状态的接口或函数。线程的状态分为正在执行、挂起、已经结束三种。利用API函数GetExitCodeThread()时获得的返回码只能...
  • 线程的状态与上下文切换

    千次阅读 2018-07-07 17:05:36
     Java线程的状态可以通过Thread实例的getState()方法获取。Thread.State所定义的线程状态包括以下几种。 NEW:一个刚创建而未启动的线程处于该状态。由于一个线程实例只能够被启动一次,因此一个线程只可能有...
  • 线程的状态以及状态转换

    千次阅读 2019-02-02 18:21:58
    线程的五种状态以及状态转换 `` 新建状态(new) 用new语句创建的线程处于新建状态,此时它和其他Java对象一样,仅仅在堆区中被分配了内存.它会一直保持这个状态直到启动了start()方法. 就绪状态(Runnable) 当一个线程...
  • JVM中线程的状态转换图 线程在一定条件下,状态会发生变化。线程一共有以下几种状态: 1、新建状态(New):新创建了一个线程对象。 2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。...
  • 如果你在工作中需要使用到线程,或是需要在多线程环境下编程,那么了解线程的生命周期(Life Cycle of Thread)以及线程的状态(Thread States)是很有必要的。  正如我们在上一篇文章中了解到的,通过实现...
  • Java线程的状态转换

    千次阅读 2012-10-22 14:04:23
    线程的状态转换对理解和正确使用线程有非常重要的意义。 线程在它的生命周期中会处于各种不同的状态。如下图所示: 3.1 新建状态(New) 用new语句创建的线程处于新建状态,此时它和其他Java对象一样,...
  • MFC如何获得线程的状态的方法

    千次阅读 2014-03-18 14:18:24
    如何获得线程的状态的方法不管是Windows API还是MFC的CWinThread类都没有给出直接获得线程状态的接口或函数。线程的状态分为正在执行、挂起、已经结束三种。利用API函数GetExitCodeThread()时获得的返回码只能判定...
  • MFC开发,如何获取子线程的状态,确保子线程只有在挂起的状态下才会被激活?
  • JVM中线程的状态转换图

    万次阅读 2014-08-28 20:29:20
    JVM中线程的状态转换图 线程在一定条件下,状态会发生变化。线程一共有以下几种状态: 1、新建状态(New):新创建了一个线程对象。 2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。...
  • 线程的状态初始态:NEW创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态。运行态:RUNNABLE在Java中,运行态包括就绪态 和 运行态。 就绪态 该状态下的线程已经获得执行所需的所有资源,只要CPU...
  • 线程的状态、分类及优先级

    千次阅读 2014-08-20 16:58:07
    转转请注明出处:  上一篇博文我们简单地...线程的状态  同样的,线程作为一项任务的执行者,从开启、运行到终结、销毁,都有它自己的生命周期。那在Java定义中,一般线程可以分为六种状态,我们通过查看
  • 线程的状态转换图

    千次阅读 2018-08-15 11:01:27
    线程变化的状态转换图例如以下:  1、新建状态(New):新创建了一个线程对象。    2、就绪状态(Runnable):线程对象创建后,其它线程调用了该对象的start()方法。 该状态的线程位于可执行线程池中,变得可执行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 64,184
精华内容 25,673
关键字:

线程的状态