blocked 和 waiting 的区别

blocked 和 waiting 两种状态从结果上来看,都是线程暂停,不会占用 cpu 资源,不过还是有一些区别的

blocked

等待 monitor 锁的阻塞线程的线程状态,处于阻塞状态的线程正在等待 monitor 锁进入 synchronized   block 或者 method ,或者在调用 object.wait 后重新进入同步块/方法。简单的说,就是线程等待 synchronized 形式的锁时的状态

下面这段代码中, t1 在等待 t0 的锁释放(synchronized代码块执行完成),那么此时 t1 的状态就是 blocked

object lock = new object();
thread t0 = new thread(new runnable() {
    @override
    public void run() {
        synchronized (lock){
            system.out.println("t0 acquire lock success");
            try {
                thread.sleep(10000);
            } catch (interruptedexception e) {
                e.printstacktrace();
            }
        }
    }
});
t0.start();
thread.sleep(100);
thread t1 = new thread(new runnable() {
    @override
    public void run() {
        synchronized (lock){
            system.out.println("t1 acquire lock success");
        }
    }
});
t1.start();
thread.sleep(100);
system.out.println("t0 state: "+t0.getstate());
system.out.println("t1 state: "+t1.getstate());
system.out.println("done.");

//output
t0 acquire lock success
t0 state: timed_waiting
t1 state: blocked
done.
t1 acquire lock success

waiting

等待中的线程状态,下面几个方法的调用会导致线程进入 waiting 状态:

  • object.wait()
  • thread.join()
  • locksupport.park()

waiting 状态中的线程在等待其他线程执行某些操作,比如在某个对象上调用 object.wait() 的线程正在等待另一个线程在该对象上调用 object.notify() 或 object.notifyall()。为 thread.join() 的线程正在等待指定的线程停止。
下面这段代码中,t0 在通过 synchronized 获取了 lock 对象的锁之后,进行了 wait 操作,导致 t0 进入 waiting 状态:

object lock = new object();
thread t0 = new thread(new runnable() {
    @override
    public void run() {
        synchronized (lock){
            system.out.println("t0 acquire lock success");
            try {
                lock.wait();
            } catch (interruptedexception e) {
                e.printstacktrace();
            }
        }
    }
});
t0.start();
thread.sleep(100);
system.out.println("t0 state: "+t0.getstate());
system.out.println("done.");

//output
t0 acquire lock success
t0 state: waiting
done.

区别

java 中除了 synchronized block/method 的锁,还提供了 juc 下的锁实现, juc.lock 下的锁功能更强大。比如支持中断,支持重入/非重入,公平/非公平等;但是 juc 下的锁和 synchronized 的实现可是不太一样的
比如下面这段代码,同样是等待锁,可是和synchronized等待锁的状态还不一样:

reentrantlock reentrantlock = new reentrantlock();
thread t0 = new thread(new runnable() {
    @override
    public void run() {
        reentrantlock.lock();

        system.out.println("t0 acquire lock success");
        try {
            thread.sleep(10000);
        } catch (interruptedexception e) {
            e.printstacktrace();
        }
    }
});
t0.start();
thread.sleep(100);
thread t1 = new thread(new runnable() {
    @override
    public void run() {
        reentrantlock.lock();
        system.out.println("t1 acquire lock success");
    }
});
t1.start();
thread.sleep(100);
system.out.println("t0 state: "+t0.getstate());
system.out.println("t1 state: "+t1.getstate());
system.out.println("done.");

//output
t0 acquire lock success
t0 state: timed_waiting
t1 state: waiting
done.

同样是加锁,在 juc 的锁实现下线程状态不太一样,所以在观察线程状态时,不止是 blocked 的状态才是等待锁, waiting/timewaiting 的状态仍然可能是等待锁的状态
不过 juc 下的锁实现,让线程暂停/等待的核心方法还是 locksupport.park , jstack 对于 parking 形式的 waiting 会有标注,所以在线程 stack 时还是能一眼看出来的:

//这里显示了等待类型
“thread-0” #11 prio=5 os_prio=31 tid=0x00007f9308110000 nid=0x5c03 waiting on condition [0x0000700007fc3000]
   java.lang.thread.state: waiting (parking)//这里虽然是waiting,但还是标注了是parking类型的
        at sun.misc.unsafe.park(native method)

而 synchronized 形式的锁在 jstack 下的输出会有所区别:

//这里显示了等待类型为monitor
“thread-1” #12 prio=5 os_prio=31 tid=0x00007f833d919800 nid=0x5a03 waiting for monitor entry [0x00007000035af000]
   java.lang.thread.state: blocked (on object monitor)//这里是blocked状态,同时显示了monitor的归属

所以在观察线程状态时,需要注意object.wait()这种waiting和juc下锁导致的waiting的区别

runnable 真的是 runnable 吗?

下面是一段 jstack 输出的例子,该线程现在正在执行 socketread0 方法(native),并且是 runnable 状态

“rmi tcp connection(2)-192.xxx.xx.xx” daemon prio=6 tid=0x000000000a3e8800 nid=0x158e50 runnable [0x000000000adbe000]
java.lang.thread.state: runnable
at java.net.socketinputstream.socketread0(native method)
at java.net.socketinputstream.read(unknown source)
at java.net.socketinputstream.read(unknown source)
at java.io.bufferedinputstream.fill(unknown source)
at java.io.bufferedinputstream.read(unknown source)
– locked (0x00000007ad784010) (a java.io.bufferedinputstream)
at java.io.filterinputstream.read(unknown source)
at sun.rmi.transport.tcp.tcptransport.handlemessages(unknown source)
at sun.rmi.transport.tcp.tcptransport$connectionhandler.run0(unknown source)
at sun.rmi.transport.tcp.tcptransport$connectionhandler.run(unknown source)
at java.util.concurrent.threadpoolexecutor.runworker(unknown source)
at java.util.concurrent.threadpoolexecutor$worker.run(unknown source)
at java.lang.thread.run(unknown source)

但其实这里的 runnable 只是 java 层面的线程状态,在操作系统或进程角度来看,该线程还是 waiting 的状态; socketinputstream 是一个 bio 的实现,当没有收到数据(或者说没有准备好可读的数据)时会发生阻塞,可这个阻塞在java线程状态里是 runnable 的状态,不过他并不会占用用户态的 cpu 时间片,内核在接受到数据后会结束这个阻塞

参考

到此这篇关于浅谈java 线程状态中可能存在的一些误区的文章就介绍到这了,更多相关java 线程状态内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!