当前位置: 首页 > 科技观察

Java线程状态可能存在的一些误解

时间:2023-03-14 22:58:02 科技观察

BLOCKED和WAITINGBLOCKED和WAITING的区别这两种状态,从结果来看,都是线程挂起,不会占用CPU资源,但是还是有一些区别的。BLOCKED等待Monitor锁被阻塞线程的线程状态,处于阻塞状态的线程正在等待Monitor锁进入synchronizedBlock或Method,或者调用Object.wait后重新进入synchronizedblock/method。简单的说就是线程等待synchronized形式的锁的状态。下面代码中,t1在等待t0的锁释放(synchronized代码块执行完毕),那么此时t1的状态为BLOCKEDObjectlock=newObject();Threadt0=newThread(newRunnable(){@Overridepublicvoidrun(){synchronized(lock){System.out.println("t0acquirelocksuccess");try{Thread.sleep(10000);}catch(InterruptedExceptione){e.printStackTrace();}}}});t0。start();Thread.sleep(100);Threadt1=newThread(newRunnable(){@Overridepublicvoidrun(){synchronized(lock){System.out.println("t1acquirelocksuccess");}}});t1.start();Thread.sleep(100);System.out.println("t0state:"+t0.getState());System.out.println("t1state:"+t1.getState());System.out.println("done.");//outputt0acquirelocksuccesst0state:TIMED_WAITINGt1state:BLOCKEDdone.t1acquirelocksuccessWAITING等待线程状态,调用以下方法会导致线程进入WAITING状态:Object.wait()Thread.join()LockSupport.park()WAITING状态的线程正在等待其他线程执行一些操作,比如一个线程在一个对象上调用Object.wait()正在等待另一个线程调用Object.notify()或对象上目的.notifyAll()是Thread.join()用于等待指定线程停止的线程。下面这段代码中,t0通过synchronized获取到锁对象的锁后,进行等待操作,导致t0进入WAITING状态:Objectlock=newObject();Threadt0=newThread(newRunnable(){@Overridepublicvoidrun(){synchronized(lock){System.out.println("t0acquirelocksuccess");try{lock.wait();}catch(InterruptedExceptione){e.printStackTrace();}}}});t0.start();Thread.sleep(100);System.out.println("t0state:"+t0.getState());System.out.println("done.");//outputt0acquirelocksuccesst0state:WAITINGdone.不同的是JAVA中synchronizedBlock/Method的锁,也提供了JUC下的锁实现,juc.lock下的锁功能更强大。比如支持中断,支持重入/不可重入,公平/不公平等;但是juc和synchronized下锁的实现是不一样的。比如下面的代码也是在等待锁,但是和synchronized等待锁是一样的。不一样:ReentrantLockreentrantLock=newReentrantLock();Threadt0=newThread(newRunnable(){@Overridepublicvoidrun(){reentrantLock.lock();System.out.println("t0acquirelocksuccess");try{Thread.sleep(10000);}catch(InterruptedExceptione){e.printStackTrace();}}});t0.start();Thread.sleep(100);Threadt1=newThread(newRunnable(){@Overridepublicvoidrun(){reentrantLock.lock();System.out.println("t1acquirelocksuccess");}});t1.start();Thread.sleep(100);System.out.println("t0state:"+t0.getState());System.out.println("t1state:"+t1.getState());System.out.println("done.");//outputt0acquirelocksuccesst0state:TIMED_WAITINGt1state:WAITINGdone.也是加锁,JUC锁实现下线程状态不一样,所以观察线程状态时,等待锁的不仅仅是BLOCKED的状态,WAITING/TIMEWAITING的状态可能还是等待锁的状态,但是JUC下的锁实现,让线程暂停/等待的核心方法是LockSupport.park,jstack是对于WAITING以PARKING的形式会被标记,所以当线程入栈还是一眼就能看出://这里显示等待类型“Thread-0”#11prio=5os_prio=31tid=0x00007f9308110000nid=0x5c03waitingoncondition[0x0000700007fc3000]java.lang.Thread.State:WAITING(parking)//虽然这里是WAITING,但是还是标记为atsun.misc.Unsafe.park(NativeMethod)的parking类型和jstack下synchronized锁的输出会不一样://这里显示等待类型是monitor"Thread-1"#12prio=5os_prio=31tid=0x00007f833d919800nid=0x5a03waitingformonitorentry[0x00007000035af000]java.lang.Thread.State:BLOCKED(onobjectmonitor)//这个是BLOCKED状态,显示的是monitor的归属,所以在观察线程状态的时候,需要注意Object.wait()这个WAITING和juc锁RUNNABLE导致的WAITING的区别是真的RUNNABLE吗?以下是jstack输出的示例。线程当前正在执行socketRead0方法(Native),处于RUNNABLE状态"RMITTCPConnection(2)-192.xxx.xx.xx"daemonprio=6tid=0x000000000a3e8800nid=0x158e50runnable[0x000000000adbe000]java.lang.Thread.State:RUNNABLEatjava.net.SocketInputStream.socketRead0(NativeMethod)atjava.net.SocketInputStream.read(UnknownSource)atjava.net.SocketInputStream。在java.io.BufferedInputStream读取(UnknownSource).fill(UnknownSource)atjava.io.BufferedInputStream.read(UnknownSource)-锁定(0x00000007ad784010)(ajava.io.BufferedInputStream)atjava.io.FilterInputStream.read(UnknownSource)atsun.rmi.transport.tcp.TCPTransports.handle)atsun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(未知来源)atsun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(未知来源)atjava.util.concurrent.ThreadPoolExecutor.runWorker(未知来源)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(UnknownSource)atjava.lang.Thread.run(UnknownSource)作者:空无链接:https://juejin.cn/post/6951187747189194782来源:掘金版权归作者所有商业转载请联系作者转载获得授权,非商业转载请注明出处。但其实这里的RUNNABLE只是JAVA层面的线程状态。从操作系统或进程的角度来看,线程仍处于WAITING状态;SocketInputStream是一个BIO实现。当没有接收到数据(或者可读数据没有准备好)时会阻塞,但是这个块在JAVA线程态是RUNNABLE状态,但是不会占用用户态的CPU时间片,内核会结束接收数据后的块。作者:无链接:https://juejin.cn/post/6951187747189194782来源:掘金版权归作者所有。商业转载请联系作者授权,非商业转载请注明出处。