導航:首頁 > 源碼編譯 > locksupport源碼詳解

locksupport源碼詳解

發布時間:2022-07-19 13:27:47

『壹』 快速學習jav的方法有哪些

作者:Yifen Hao
鏈接:https://www.hu.com/question/57483039/answer/153055031
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。

1. 做完的東西記得寫筆記。
比如在Spring中配置了redis集群,如果下次配置,還要去網上搜,效率低,不如把那部分代碼寫到筆記里。
今天在idea中用插件把代碼部署到docker里,也是一邊谷歌,一邊記筆記。
又比如一些常用的命令,總是會記不起來,用到的時候去搜谷歌,效率真的很低。不如記錄下來。
我自己用的notepad++記錄筆記,一直開著,需要的時候用快捷鍵直接切換到前台,從來不會卡,從來不擔心內容丟失。馬上就能記下來。記得時候不用太擔心格式,畢竟文字最重要。記錄了之後,後面整理好,用markdown格式寫到雲筆記里。
2.堅持學習新東西
程序員只有一種死法,土死的。
Spring是個好東西,但是配置太繁瑣了,如果自己想重新搞一個應用,或者做一些功能小demo,先要弄一大堆配置,等弄完配置,心都冷了。Spring Boot作為新技術,大大簡化了配置,啟動一個web應用都不用寫配置。我想驗證一個功能,點幾下滑鼠就能啟動了。
學習java8,jdk8引入了lambda表達式,大大減少了繁雜代碼,添加函數式方法,對集合的操作大大簡化。新的time包由joda time作者所寫,比之前的date,calender好用太多。
好用的東西還有guava庫。
3.學習其他語言和編程範式
python,kotlin,多了解一下編程範式。java寫多了,人真的變笨了。
4.多看優秀書籍
Java並發編程實戰,Effective Java,重構,演算法,HTTP權威指南等等
5.多看源碼
我覺得自己有個優點,也算是個缺點,我接觸到一個東西,我就特別想知道,它原理是什麼,怎麼實現的,忍不住點進去源碼看看。
前不久把java並發的工具類看了。從UNSAFE開始,到LockSupport和原子工具類,到AQS實現,然後基於AQS的Lock,CountdownLatch,Semaphore,然後是基於Lock的阻塞隊列實現等。
Java的容器代碼,我基本也看過很多。
之前也看了Zookeeper的源碼。
Spring的源碼結構也基本了解了。

『貳』 Lock的await/singal 和 Object的wait/notify 的區別

在使用Lock之前,我們都使用Object 的wait和notify實現同步的。舉例來說,一個procer和consumer,consumer發現沒有東西了,等待,proer生成東西了,喚醒。
線程consumer 線程procer
synchronize(obj){
obj.wait();//沒東西了,等待
} synchronize(obj){
obj.notify();//有東西了,喚醒
}
有了lock後,世道變了,現在是:
lock.lock();
condition.await();
lock.unlock(); lock.lock();
condition.signal();
lock.unlock();
為了突出區別,省略了若干細節。區別有三點:
1. lock不再用synchronize把同步代碼包裝起來;
2. 阻塞需要另外一個對象condition;
3. 同步和喚醒的對象是condition而不是lock,對應的方法是await和signal,而不是wait和notify。
為什麼需要使用condition呢?簡單一句話,lock更靈活。以前的方式只能有一個等待隊列,在實際應用時可能需要多個,比如讀和寫。為了這個靈活性,lock將同步互斥控制和等待隊列分離開來,互斥保證在某個時刻只有一個線程訪問臨界區(lock自己完成),等待隊列負責保存被阻塞的線程(condition完成)。
通過查看ReentrantLock的源代碼發現,condition其實是等待隊列的一個管理者,condition確保阻塞的對象按順序被喚醒。
在Lock的實現中,LockSupport被用來實現線程狀態的改變,後續將更進一步研究LockSupport的實現機制。

『叄』 java里是怎麼通過condition介面是獲取監視器方法的

ReentrantLock和condition是配合著使用的,就像wait和notify一樣,提供一種多線程間通信機制。

ReentrantLock 的lock方法有兩種實現:公平鎖與非公平鎖
看newCondition的源碼實現:

final ConditionObject newCondition() {
return new ConditionObject();}

其實就是只實例化一個個conditionObject對象綁定到lock罷了。也就是拿到了監視器,再深入到conditionObject這個裡面實現看看await方法:

public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); int interruptMode = 0; while (!isOnSyncQueue(node)) {
LockSupport.park(this); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break; }
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT; if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);}

大概就是將當前線程加入等待隊列,其中做一些邏輯判斷,再來看看喚醒的方法:singal和singalAll:

public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}

其實就是將等待隊列裡面的線程依次喚醒罷了,doSingalAll:

private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}

transferForSignal將線程轉移到syncQueue重新排隊,這里主要用到CAS(lock free)演算法改變狀態:

final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;

/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}

篇幅有限,沒有詳細描述...反正多看看源碼吧,結合著實例分析

『肆』 Lock的await/singal 和 Object的wait/notify 的區別

Lock的await/singal 和 Object的wait/notify 的區別

在使用Lock之前,我們都使用Object 的wait和notify實現同步的。舉例來說,一個procer和consumer,consumer發現沒有東西了,等待,proer生成東西了,喚醒。

線程consumer 線程procer

synchronize(obj){
obj.wait();//沒東西了,等待
} synchronize(obj){
obj.notify();//有東西了,喚醒
}

有了lock後,世道變了,現在是:

lock.lock();
condition.await();
lock.unlock(); lock.lock();
condition.signal();
lock.unlock();

為了突出區別,省略了若干細節。區別有三點:

1. lock不再用synchronize把同步代碼包裝起來;
2. 阻塞需要另外一個對象condition;
3. 同步和喚醒的對象是condition而不是lock,對應的方法是await和signal,而不是wait和notify。


什麼需要使用condition呢?簡單一句話,lock更靈活。以前的方式只能有一個等待隊列,在實際應用時可能需要多個,比如讀和寫。為了這個靈活
性,lock將同步互斥控制和等待隊列分離開來,互斥保證在某個時刻只有一個線程訪問臨界區(lock自己完成),等待隊列負責保存被阻塞的線程
(condition完成)。

通過查看ReentrantLock的源代碼發現,condition其實是等待隊列的一個管理者,condition確保阻塞的對象按順序被喚醒。

在Lock的實現中,LockSupport被用來實現線程狀態的改變,後續將更進一步研究LockSupport的實現機制。

『伍』 java並發包源碼怎麼讀

1. 各種同步控制工具的使用

1.1 ReentrantLock

ReentrantLock感覺上是synchronized的增強版,synchronized的特點是使用簡單,一切交給JVM去處理,但是功能上是比較薄弱的。在JDK1.5之前,ReentrantLock的性能要好於synchronized,由於對JVM進行了優化,現在的JDK版本中,兩者性能是不相上下的。如果是簡單的實現,不要刻意去使用ReentrantLock。

相比於synchronized,ReentrantLock在功能上更加豐富,它具有可重入、可中斷、可限時、公平鎖等特點。

首先我們通過一個例子來說明ReentrantLock最初步的用法:

package test;

import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i = 0;

@Override public void run() { for (int j = 0; j < 10000000; j++)
{ lock.lock(); try
{
i++;
} finally
{ lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}

}

有兩個線程都對i進行++操作,為了保證線程安全,使用了ReentrantLock,從用法上可以看出,與synchronized相比,ReentrantLock就稍微復雜一點。因為必須在finally中進行解鎖操作,如果不在finally解鎖,有可能代碼出現異常鎖沒被釋放,而synchronized是由JVM來釋放鎖。

那麼ReentrantLock到底有哪些優秀的特點呢?

1.1.1 可重入

單線程可以重復進入,但要重復退出

lock.lock();
lock.lock();try{
i++;

}
finally{
lock.unlock();
lock.unlock();
}

由於ReentrantLock是重入鎖,所以可以反復得到相同的一把鎖,它有一個與鎖相關的獲取計數器,如果擁有鎖的某個線程再次得到鎖,那麼獲取計數器就加1,然後鎖需要被釋放兩次才能獲得真正釋放(重入鎖)。這模仿了synchronized的語義;如果線程進入由線程已經擁有的監控器保護的 synchronized 塊,就允許線程繼續進行,當線程退出第二個(或者後續)synchronized塊的時候,不釋放鎖,只有線程退出它進入的監控器保護的第一個synchronized塊時,才釋放鎖。

public class Child extends Father implements Runnable{ final static Child child = new Child();//為了保證鎖唯一
public static void main(String[] args) { for (int i = 0; i < 50; i++) { new Thread(child).start();
}
}
public synchronized void doSomething() {
System.out.println("1child.doSomething()");
doAnotherThing(); // 調用自己類中其他的synchronized方法
}
private synchronized void doAnotherThing() { super.doSomething(); // 調用父類的synchronized方法
System.out.println("3child.doAnotherThing()");
}
@Override
public void run() {
child.doSomething();
}
}class Father { public synchronized void doSomething() {
System.out.println("2father.doSomething()");
}
}

我們可以看到一個線程進入不同的synchronized方法,是不會釋放之前得到的鎖的。所以輸出還是順序輸出。所以synchronized也是重入鎖

輸出:

1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
...

1.1.2.可中斷

與synchronized不同的是,ReentrantLock對中斷是有響應的。中斷相關知識查看[高並發Java 二] 多線程基礎

普通的lock.lock()是不能響應中斷的,lock.lockInterruptibly()能夠響應中斷。

我們模擬出一個死鎖現場,然後用中斷來處理死鎖

package test;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int lock; public Test(int lock)
{ this.lock = lock;
} @Override
public void run()
{ try
{ if (lock == 1)
{
lock1.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock2.lockInterruptibly();
} else
{
lock2.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock1.lockInterruptibly();
}
} catch (Exception e)
{ // TODO: handle exception
} finally
{ if (lock1.isHeldByCurrentThread())
{
lock1.unlock();
} if (lock2.isHeldByCurrentThread())
{
lock2.unlock();
}
System.out.println(Thread.currentThread().getId() + ":線程退出");
}
} public static void main(String[] args) throws InterruptedException {
Test t1 = new Test(1);
Test t2 = new Test(2);
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
Thread.sleep(1000); //DeadlockChecker.check();
} static class DeadlockChecker
{ private final static ThreadMXBean mbean = ManagementFactory
.getThreadMXBean(); final static Runnable deadlockChecker = new Runnable()
{ @Override
public void run()
{ // TODO Auto-generated method stub
while (true)
{ long[] deadlockedThreadIds = mbean.findDeadlockedThreads(); if (deadlockedThreadIds != null)
{
ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds); for (Thread t : Thread.getAllStackTraces().keySet())
{ for (int i = 0; i < threadInfos.length; i++)
{ if(t.getId() == threadInfos[i].getThreadId())
{
t.interrupt();
}
}
}
} try
{
Thread.sleep(5000);
} catch (Exception e)
{ // TODO: handle exception
}
}

}
};
public static void check()
{
Thread t = new Thread(deadlockChecker);
t.setDaemon(true);
t.start();
}
}

}

上述代碼有可能會發生死鎖,線程1得到lock1,線程2得到lock2,然後彼此又想獲得對方的鎖。

我們用jstack查看運行上述代碼後的情況

下面舉個例子:

package test;import java.util.concurrent.CyclicBarrier;public class Test implements Runnable{ private String soldier; private final CyclicBarrier cyclic; public Test(String soldier, CyclicBarrier cyclic)
{ this.soldier = soldier; this.cyclic = cyclic;
} @Override
public void run()
{ try
{ //等待所有士兵到齊
cyclic.await();
dowork(); //等待所有士兵完成工作
cyclic.await();
} catch (Exception e)
{ // TODO Auto-generated catch block
e.printStackTrace();
}

} private void dowork()
{ // TODO Auto-generated method stub
try
{
Thread.sleep(3000);
} catch (Exception e)
{ // TODO: handle exception
}
System.out.println(soldier + ": done");
} public static class BarrierRun implements Runnable
{ boolean flag; int n; public BarrierRun(boolean flag, int n)
{ super(); this.flag = flag; this.n = n;
} @Override
public void run()
{ if (flag)
{
System.out.println(n + "個任務完成");
} else
{
System.out.println(n + "個集合完成");
flag = true;
}

}

} public static void main(String[] args)
{ final int n = 10;
Thread[] threads = new Thread[n]; boolean flag = false;
CyclicBarrier barrier = new CyclicBarrier(n, new BarrierRun(flag, n));
System.out.println("集合"); for (int i = 0; i < n; i++)
{
System.out.println(i + "報道");
threads[i] = new Thread(new Test("士兵" + i, barrier));
threads[i].start();
}
}

}

列印結果:

集合

士兵5: done士兵7: done士兵8: done士兵3: done士兵4: done士兵1: done士兵6: done士兵2: done士兵0: done士兵9: done10個任務完成

1.7 LockSupport

提供線程阻塞原語

和suspend類似

LockSupport.park();
LockSupport.unpark(t1);

與suspend相比不容易引起線程凍結

LockSupport的思想呢,和Semaphore有點相似,內部有一個許可,park的時候拿掉這個許可,unpark的時候申請這個許可。所以如果unpark在park之前,是不會發生線程凍結的。

下面的代碼是[高並發Java 二] 多線程基礎中suspend示例代碼,在使用suspend時會發生死鎖。

而使用LockSupport則不會發生死鎖。

另外

park()能夠響應中斷,但不拋出異常。中斷響應的結果是,park()函數的返回,可以從Thread.interrupted()得到中斷標志。

在JDK當中有大量地方使用到了park,當然LockSupport的實現也是使用unsafe.park()來實現的。

public static void park() { unsafe.park(false, 0L);
}

1.8 ReentrantLock 的實現

下面來介紹下ReentrantLock的實現,ReentrantLock的實現主要由3部分組成:

『陸』 線程池中空閑的線程處於什麼狀態

一:阻塞狀態,線程並沒有銷毀,也沒有得到CPU時間片執行;

源碼追蹤:
for (;;) {
...
workQueue.take();
...
}
public E take()...{
...
while (count.get() == 0) { / /這里就是任務隊列中的消息數量
notEmpty.await();
}
...
}

public final void await()...{
...
LockSupport.park(this);

...
}

繼續往下:
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, 0L);
setBlocker(t, null);
}

private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();

//線程調用該方法,線程將一直阻塞直到超時,或者是中斷條件出現。

public native void park(boolean isAbsolute, long time);
上面就是java11線程池中阻塞的源碼追蹤;

二.對比object的wait()方法:
@FastNative
public final native void wait(long timeout, int nanos) throws InterruptedException;
還有Thread的sleep() 方法:
@FastNative
private static native void sleep(Object lock, long millis, int nanos)throws...;
可見,線程池中使用的阻塞方式並不是Object中的wait(),也不是Thread.sleep() ;

這3個方法最終實現都是通過c&c++實現的native方法.

三.在<<Java虛擬機(第二版)>>中,對線程狀態有以下介紹:
12.4.3狀態轉換
Java語言定義了5種線程狀態,在任意一個時間點,一個線程只能有且只有其中的一種
狀態,這5種狀態分別如下。
1)新建(New):創建後尚未啟動的線程處於這種狀態。
2)運行(Runable):Runable包括了操作系統線程狀態中的Running和Ready,也就是處於此
狀態的線程有可能正在執行,也有可能正在等待著CPU為它分配執行時間。
3)無限期等待(Waiting):處於這種狀態的線程不會被分配CPU執行時間,它們要等待被
其他線程顯式地喚醒。以下方法會讓線程陷入無限期的等待狀態:
●沒有設置Timeout參數的Object.wait()方法。
●沒有設置Timeout參數的Thread.join()方法。
●LockSupport.park()方法。
4)限期等待(Timed Waiting):處於這種狀態的線程也不會被分配CPU執行時間,不過無
須等待被其他線程顯式地喚醒,在一定時間之後它們會由系統自動喚醒。以下方法會讓線程
進入限期等待狀態:
●Thread.sleep()方法。
●設置了Timeout參數的Object.wait()方法。
●設置了Timeout參數的Thread.join()方法。
●LockSupport.parkNanos()方法。
●LockSupport.parkUntil()方法。
5)阻塞(Blocked):線程被阻塞了,「阻塞狀態」與「等待狀態」的區別是:「阻塞狀態」在等
待著獲取到一個排他鎖,這個事件將在另外一個線程放棄這個鎖的時候發生;而「等待狀
態」則是在等待一段時間,或者喚醒動作的發生。在程序等待進入同步區域的時候,線程將
進入這種狀態。
結束(Terminated):已終止線程的線程狀態,線程已經結束執行。

閱讀全文

與locksupport源碼詳解相關的資料

熱點內容
鎖定伺服器是什麼意思 瀏覽:375
場景檢測演算法 瀏覽:607
解壓手機軟體觸屏 瀏覽:338
方舟pv怎麼轉伺服器 瀏覽:99
數據挖掘中誤差值演算法函數 瀏覽:118
php開發套件 瀏覽:190
伺服器的spi板是什麼 瀏覽:896
解壓縮全能王中文密碼是什麼 瀏覽:80
javaftp伺服器上傳文件 瀏覽:103
演算法設計中文版pdf 瀏覽:81
視頻壓縮形式怎麼改 瀏覽:368
perl程序員 瀏覽:789
電子表格對比命令 瀏覽:610
php循環輸出數組內容 瀏覽:750
電腦加密能不能強制關掉 瀏覽:616
趣味單人解壓桌游 瀏覽:212
oppo手機谷歌伺服器無法核實什麼 瀏覽:320
軟體怎麼加密華為 瀏覽:222
掃地機怎麼安裝app 瀏覽:319
考研結合特徵值計演算法 瀏覽:517