Java 线程状态

来源:互联网 发布:淘宝网站版面特点 编辑:程序博客网 时间:2024/05/01 21:58

Java线程的状态

Java[2]

 

上图来源于网络资源,本人加了一些文字注释有关状态的转换

针对上图进行说明:

  • 线程通过start()启动后进入到可运行状态,在此状态下,一旦锁释放,由JVM调度程序自动调度。如果有多个可运行程序,并且优先级相同,由JVM进行选择;
  • 线程运行的唯一前置状态是可运行,因此在等待或者睡眠状态的线程,在运行之前必须先为可运行状态。这就是如果一个线程在锁上调用了wait()方法,则将无休止的等待,如果没有其他线程把它唤醒,他将永远不会再运行。

public class PrintNum {

    private byte[] lock = new byte[0]; // 自定义锁对象,这样代价最小,也可已使用当前对象this

    public void demo() {
        PrintThread a = new PrintThread("a");
        PrintThread b = new PrintThread("b");
        PrintThread c = new PrintThread("c");
        PrintThread d = new PrintThread("d");
        a.start();
        b.start();
        c.start();
        d.start();
    }

    class PrintThread extends Thread {
        public PrintThread(String name) {
            setName(name);
        }

        public void run() {
            synchronized (lock) {
                for (int i = 0; i < 100; i++) {
                    if (i % 10 == 0 && 0 != i) {
                        try {
                             System.out.println(this.getName() + ": i="+i+",lock.wait()");
                            lock.wait(); // 暂时释放资源

                             System.out.println(this.getName() + ": i="+i+",notify()");
                            lock.notify(); // 唤醒另外一个进程

                       } catch (Throwable e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(this.getName() + ": " + i);
                }
            }
        }
    }
    public static void main(String[] args) {
        PrintNum printNum = new PrintNum();
        printNum.demo();
    }
}

如上代码分析:

  1. 依次启动了a,b,c,d 4个线程,线程进入到可运行状态。由于a最先被启动,所以它往往最先被执行;
  2. 当a运行到i=10的时候进入到阻塞状态,没有机会执行下面的lock.notify();
  3. 由于a把锁释放,因此JVM自动从可运行的线程中选择一个运行,可能是b,c,d,假设是b;
  4. 当b运行到i=10的时候进入到阻塞状态,没有机会执行下面的lock.notify();
  5. 由于b把锁释放,因此JVM自动从可运行的线程中选择一个运行,可能是c,d,假设是c;
  6. 当c运行到i=10的时候进入到阻塞状态,没有机会执行下面的lock.notify();
  7. 由于c把锁释放,因此JVM自动从可运行的线程中选择一个运行,这时只有b在可运行队列中,所以是d;
  8. 当d运行到i=10的时候进入到阻塞状态,没有机会执行下面的lock.notify();
  9. 此时可运行队列为空,没有线程需要JVM调度到运行状态;
  10. 等待队列中有4个线程,但它们都没有被唤醒,所以将持续等待,程序无法执行完毕而退出,进入死锁状态。

 

解决办法:

将notify()调用放在wait()调用之前,这样可以保证可运行队列中始终有线程可被调度为运行状态。

  • 线程由‘新’到‘可运行’是通过Thread.start()方法实现的;
  • 线程由‘可运行’到‘运行’,是由操作系统负责调度的,这部分对JVM来说是透明的;
  • 线程由‘运行’到‘等待’,是通过lock.wait方法实现的,wait方法有多个重载方法,wait(0)/wait()代表一直等待,直到被notify;
  • 线程由‘等待’到‘可运行’,是通过lock.notify/notifyAll方法实现的;
  • 线程由’运行’到’睡眠’,是通过Thread.sleep(long)方法实现的,sleep方法的参数是指定了睡眠的时间。在睡眠过程中,对锁的占有并不释放,因此其他同步该锁的线程没有机会运行,但其它非同步该锁的线程是有机会运行的(任何优先级的线程都有机会,因为它不参与CPU的竞争)。当睡眠时间到期,则自动返回到可运行状态。尽管占有锁,但需要和其他非同步线程竞争CPU资源,是否能够运行取决于操作系统的调度;
  • 线程由‘运行’到‘可运行’是通过Thread.yield()方法实现的,yield不会释放锁,因此其他同步的线程没有机会运行,但具有相同优先级的其他线程有机会运行。但是,实际中无法保证yield()能达到让步目的,因为让步的线程还有可能被线程调度程序再次选中(往往是这样)。
  • 另外,Thread.join的作用是等待该线程终止,例如当前A线程执行到某步时需要B线程运行结果,因此需要其完成才能继续进行,这时在A线程内调用B.join方法等待B执行结束再执行。join方法有多个重载方法,可以指定等待的时间join(long);join(0)/join()代表一直等待。 本人感觉这个方法特别适合于等待可能超时的请求上,为了防止无休止的等待而占用系统的资源,可以对这样的请求用一个单独的线程,通过join方法指定等待的时间,超过时间后,跳过. 通过状态的检查来判断其是否正常结束,如果没有结束,则退出,返回超时的应答。
原创粉丝点击