如何讓線程主動讓出CPU
由於等待一個鎖的線程只有在獲得這把鎖之后,才能恢復運行,所以讓持有鎖的線程在不再需要鎖的時候及時釋放鎖是很重要的。在以下情況下,持有鎖的線程會釋放鎖:
(1)執行完同步代碼塊,就會釋放鎖。(synchronized執行完畢解鎖)
(2)在執行同步代碼塊的過程中,遇到異常而導致線程終止,鎖也會被釋放。(exception異常)
(3)在執行同步代碼塊的過程中,執行了鎖所屬對象的wait()方法,這個線程會釋放鎖,進入對象的等待池。(wait方法被調用)
除了以上情況以外,只要持有鎖的線程還沒有執行完同步代碼塊,就不會釋放鎖。
在下面情況下,線程是不會釋放鎖的:
(1)執行同步代碼塊的過程中,執行了Thread.sleep()方法,當前線程放棄CPU,開始睡眠,在睡眠中不會釋放鎖。
(2)在執行同步代碼塊的過程中,執行了Thread.yield()方法,當前線程放棄CPU,但不會釋放鎖。
(3)在執行同步代碼塊的過程中,其他線程執行了當前線程對象的suspend()方法,當前線程被暫停,但不會釋放鎖。
Thread.sleep();
sleep就是正在執行的線程主動讓出cpu,cpu去執行其他線程,在sleep指定的時間過后,cpu才會回到這個線程上繼續往下執行,如果當前線程進入了同步鎖,sleep方法並不會釋放鎖,即使當前線程使用sleep方法讓出了cpu,但其他被同步鎖擋住了的線程也無法得到執行。
在多線程爭用的情況,擁有鎖的線程進行一些耗時操作,會極大降低吞吐量(amdahl定律),如果在同步塊中使用sleep就是一種糟糕的做法,它不會釋放鎖卻阻止其他線程獲得鎖。
Thread.yield();
yeild是個native靜態方法,這個方法是想把自己占有的cpu時間釋放掉,然后和其他線程一起競爭(注意yeild的線程還是有可能爭奪到cpu,注意與sleep區別)。在javadoc中也說明了,yeild是個基本不會用到的方法,一般在debug和test中使用。
Object.wait();
wait會把當前的鎖釋放掉同時阻塞住,讓出CPU;當別的線程調用該 Object 的 notify/notifyAll 之后,有可能得到 CPU,同時重新獲得鎖。
當然同樣的
condition.await();
也會釋放鎖並且讓出CPU;
當然很多競爭鎖失敗的線程最終也會阻塞住,但是這不是主動讓出CPU,不在討論范圍內。
Thread.stop();
太過暴力,不推薦使用,同時會釋放所有鎖。容易導致數據不一致,具體請查看[高並發Java 二] 多線程基礎
Thread.currentThread().suspend();
掛起線程,suspend不會釋放鎖,suspend容易發生死鎖,不推薦使用。原因請查看[高並發Java 二] 多線程基礎
LockSupport.park();
與suspend一樣掛起線程,但是park/unpark不會發生死鎖,原因請查看[高並發Java 五] JDK並發包1
Java中Thread的狀態有:
NEW
RUNNABLE
WAITING:Object.wait(),LockSupport.park()
TIMED_WAITING:Thread.sleep(long),Object.wait(long),LockSupport.parkNanos(long)
BLOCKED:Object.wait()
TERMINATED:Thread.stop()
如何讓線程主動放棄持有的鎖
Object.wait();
condition.await();
Thread.stop();
上面已經說了
當然所有釋放鎖的操作都可以:
reentrantLock.unlock();
semaphore.release();
要注意的是以下操作不會釋放鎖,在上面已經談到了,這里再列舉以下:
Thread.sleep();
Thread.yield();
Thread.currentThread().suspend();
LockSupport.park();