java多線程(2)-線程同步與線程安全

前言

前面我們?cè)?a href="%5Bhttp://m.itdecent.cn/p/dd33a2c2562e%5D(http://m.itdecent.cn/p/dd33a2c2562e)" target="_blank" rel="nofollow">java多線程相關(guān)(1)-synchronized,wait,notify,volatile中簡(jiǎn)單介紹了最簡(jiǎn)單的多線程同步的方法;就我個(gè)人的理解,多線程同步的目的有兩個(gè) 1.保證多線程的執(zhí)行順序 2.維護(hù)多線程可見(jiàn)變量(如volalite變量,或者更高一級(jí)的synchronized修飾變量)的安全;我將在本章中嘗試寫(xiě)出我個(gè)人的理解

線程同步常見(jiàn)示例

CountDownLatch示例

CountDownLatch等待多個(gè)線程執(zhí)行完成再繼續(xù)執(zhí)行,被多個(gè)線程阻塞,類(lèi)似于多個(gè)線程join


mport java.util.concurrent.CountDownLatch;

public class TestMultiThread1 {

private static final CountDownLatch countDwonLatch = new CountDownLatch(2);

private static class TestCountDownLatchThread extends Thread{

@Override

public void run() {

// TODO Auto-generated method stub

System.out.println("TestCountDownLatchThread 子線程開(kāi)始執(zhí)行");

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("TestCountDownLatchThread 子線程執(zhí)行完成");

countDwonLatch.countDown();

}

}

public static void main(String[] args) {

// TODO Auto-generated method stub

System.out.println("主線程開(kāi)始執(zhí)行");

new TestCountDownLatchThread().start();

new TestCountDownLatchThread().start();

try {

countDwonLatch.await();

System.out.println("主線程等待子線程執(zhí)行完成");

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("主線程繼續(xù)執(zhí)行");

}

}

結(jié)果為:


主線程開(kāi)始執(zhí)行

TestCountDownLatchThread 子線程開(kāi)始執(zhí)行

TestCountDownLatchThread 子線程開(kāi)始執(zhí)行

TestCountDownLatchThread 子線程執(zhí)行完成

TestCountDownLatchThread 子線程執(zhí)行完成

主線程等待子線程執(zhí)行完成

主線程繼續(xù)執(zhí)行

改為countDwonLatch.await(1000, TimeUnit.MILLISECONDS);的后結(jié)果為:


主線程開(kāi)始執(zhí)行

TestCountDownLatchThread 子線程開(kāi)始執(zhí)行

TestCountDownLatchThread 子線程開(kāi)始執(zhí)行

主線程等待子線程執(zhí)行完成

主線程繼續(xù)執(zhí)行

TestCountDownLatchThread 子線程執(zhí)行完成

TestCountDownLatchThread 子線程執(zhí)行完成

CyclicBarrier示例

CyclicBarrier一般用于一組線程互相等待至某個(gè)狀態(tài),然后這一組線程再同時(shí)執(zhí)行


private static class TestCyclicBarrierThread extends Thread{

private int worktime;

TestCyclicBarrierThread(int worktime) {

this.worktime = worktime;

}

@Override

public void run() {

// TODO Auto-generated method stub

System.out.println("TestCyclicBarrierThread 子線程開(kāi)始執(zhí)行");

try {

Thread.sleep(worktime*1000);

System.out.println("TestCyclicBarrierThread 子線程執(zhí)行"+worktime+"s");

try {

cyclicBarrier.await();

} catch (BrokenBarrierException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("TestCountDownLatchThread 子線程繼續(xù)執(zhí)行");

}

}

public static void main(String[] args) {

for(int i = 0;i<4;i++) {

new TestCyclicBarrierThread(i+1).start();

}

}


TestCyclicBarrierThread 子線程開(kāi)始執(zhí)行

TestCyclicBarrierThread 子線程開(kāi)始執(zhí)行

TestCyclicBarrierThread 子線程開(kāi)始執(zhí)行

TestCyclicBarrierThread 子線程開(kāi)始執(zhí)行

TestCyclicBarrierThread 子線程執(zhí)行1s

TestCyclicBarrierThread 子線程執(zhí)行2s

TestCyclicBarrierThread 子線程執(zhí)行3s

TestCyclicBarrierThread 子線程執(zhí)行4s

TestCountDownLatchThread 子線程繼續(xù)執(zhí)行

TestCountDownLatchThread 子線程繼續(xù)執(zhí)行

TestCountDownLatchThread 子線程繼續(xù)執(zhí)行

TestCountDownLatchThread 子線程繼續(xù)執(zhí)行

將CyclicBarrier對(duì)象改為


TestCyclicBarrierThread 子線程開(kāi)始執(zhí)行

TestCyclicBarrierThread 子線程開(kāi)始執(zhí)行

TestCyclicBarrierThread 子線程開(kāi)始執(zhí)行

TestCyclicBarrierThread 子線程開(kāi)始執(zhí)行

TestCyclicBarrierThread 子線程執(zhí)行1s

TestCyclicBarrierThread 子線程執(zhí)行2s

TestCyclicBarrierThread 子線程執(zhí)行3s

TestCyclicBarrierThread 子線程執(zhí)行4s

TestCountDownLatchThread 子線程做額外操作 //選一個(gè)子線程做額外操作

TestCountDownLatchThread 子線程繼續(xù)執(zhí)行

TestCountDownLatchThread 子線程繼續(xù)執(zhí)行

TestCountDownLatchThread 子線程繼續(xù)執(zhí)行

TestCountDownLatchThread 子線程繼續(xù)執(zhí)行

Semaphore示例

Semaphore感覺(jué)其實(shí)和鎖有點(diǎn)類(lèi)似,它一般用于控制對(duì)某組資源的訪問(wèn)權(quán)限(不是一個(gè),而是一組同類(lèi)型資源);用一組資源來(lái)維護(hù)線程的同步


private static final Semaphore semaphore = new Semaphore(5); //機(jī)器數(shù)

private static class Worker extends Thread{

private int num;

private Semaphore semaphore;

public Worker(int num,Semaphore semaphore){

this.num = num;

this.semaphore = semaphore;

}

@Override

public void run() {

try {

semaphore.acquire(); //獲得信號(hào)量

System.out.println("工人"+this.num+"占用一個(gè)機(jī)器在生產(chǎn)...");

Thread.sleep(2000);

System.out.println("工人"+this.num+"釋放出機(jī)器");

semaphore.release(); //釋放信號(hào)量

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

for(int i=0;i<8;i++)

new Worker(i,semaphore).start();

從上面幾個(gè)示例我們可以看出,java為我們提供了很多種線程同步方式的類(lèi),這些都在java.util.concurrent包下面,其示意圖可以如圖表示

concurrent示意圖.png

我們可以看到其中的AQS,非阻塞數(shù)據(jù)結(jié)構(gòu)和原子變量類(lèi)(java.util.concurrent.atomic包中的類(lèi)),這些concurrent包中的基礎(chǔ)類(lèi)都是使用這種模式來(lái)實(shí)現(xiàn)的,而concurrent包中的高層類(lèi)又是依賴于這些基礎(chǔ)類(lèi)來(lái)實(shí)現(xiàn)的

下面我們就來(lái)看看AQS

AQS(線程同步器)

AQS的定義

AQS(AbstractQueuedSynchronizer),AQS是JDK下提供的一套用于實(shí)現(xiàn)基于FIFO等待隊(duì)列的阻塞鎖和相關(guān)的同步器的一個(gè)同步框架。這個(gè)抽象類(lèi)被設(shè)計(jì)為作為一些可用原子int值來(lái)表示狀態(tài)的同步器的基類(lèi);大致結(jié)構(gòu)如圖

AQS組織結(jié)構(gòu).png

一個(gè)volatile資源,以及一個(gè)線程隊(duì)列,維護(hù)征用該資源的多個(gè)線程;

該資源可以表現(xiàn)任何狀態(tài)。比如, CountDownLatch表示剩余持有該資源的線程數(shù),Semaphore 用它來(lái)表現(xiàn)剩余的許可數(shù),ReentrantLock 用它來(lái)表現(xiàn)擁有它的線程已經(jīng)請(qǐng)求了多少次鎖;FutureTask 用它來(lái)表現(xiàn)任務(wù)的狀態(tài)(尚未開(kāi)始、運(yùn)行、完成和取消)等等;這些都是根據(jù)AQS自定義出來(lái)的

AbstractQueuedSynchronizer類(lèi)

AQS定義兩種資源共享方式:Exclusive(獨(dú)占,只有一個(gè)線程能執(zhí)行,如ReentrantLock)和Share(共享,多個(gè)線程可同時(shí)執(zhí)行,如Semaphore/CountDownLatch)。

不同的自定義同步器爭(zhēng)用共享資源的方式也不同。自定義同步器在實(shí)現(xiàn)時(shí)只需要實(shí)現(xiàn)共享資源state的獲取與釋放方式即可,至于具體線程等待隊(duì)列的維護(hù)(如獲取資源失敗入隊(duì)/喚醒出隊(duì)等),AQS已經(jīng)在頂層實(shí)現(xiàn)好了。自定義同步器實(shí)現(xiàn)時(shí)主要實(shí)現(xiàn)以下幾種方法:

  • isHeldExclusively():該線程是否正在獨(dú)占資源。只有用到condition才需要去實(shí)現(xiàn)它。
  • tryAcquire(int):獨(dú)占方式。嘗試獲取資源,成功則返回true,失敗則返回false。
  • tryRelease(int):獨(dú)占方式。嘗試釋放資源,成功則返回true,失敗則返回false。
  • tryAcquireShared(int):共享方式。嘗試獲取資源。負(fù)數(shù)表示失??;0表示成功,但沒(méi)有剩余可用資源;正數(shù)表示成功,且有剩余資源。
  • tryReleaseShared(int):共享方式。嘗試釋放資源,如果釋放后允許喚醒后續(xù)等待結(jié)點(diǎn)返回true,否則返回false。

一般來(lái)說(shuō),獨(dú)占式和共享式實(shí)現(xiàn)一種即可,當(dāng)然也可以都實(shí)現(xiàn)(如ReentrantReadWriteLock,表示讀寫(xiě)鎖)

AbstractQueuedSynchronizer代碼分析

以獨(dú)占模式為例


1209 /**

1210 * Acquires in exclusive mode, ignoring interrupts. Implemented

1211 * by invoking at least once {@link #tryAcquire},

1212 * returning on success. Otherwise the thread is queued, possibly

1213 * repeatedly blocking and unblocking, invoking {@link

1214 * #tryAcquire} until success. This method can be used

1215 * to implement method {@link Lock#lock}.

1216 *

1217 * @param arg the acquire argument. This value is conveyed to

1218 * {@link #tryAcquire} but is otherwise uninterpreted and

1219 * can represent anything you like.

1220 */

1221 public final void acquire(int arg) {

1222 if (!tryAcquire(arg) &&

1223 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

1224 selfInterrupt();

1225 }

函數(shù)流程如下:

1.tryAcquire()嘗試直接去獲取資源,如果成功則直接返回;

2.addWaiter()將該線程加入等待隊(duì)列的尾部,并標(biāo)記為獨(dú)占模式;

3.acquireQueued()使線程在等待隊(duì)列中獲取資源,一直獲取到資源后才返回。如果在整個(gè)等待過(guò)程中被中斷過(guò),則返回true,否則返回false。

4.如果線程在等待過(guò)程中被中斷過(guò),它是不響應(yīng)的。只是獲取資源后才再進(jìn)行自我中斷selfInterrupt(),將中斷補(bǔ)上

tryAcquire

由各個(gè)同步器自己實(shí)現(xiàn),如ReentrantLock中的tryAcquire


213 protected final boolean tryAcquire(int acquires) {

214 return nonfairTryAcquire(acquires);

215 }


126 /**

127 * Performs non-fair tryLock. tryAcquire is implemented in

128 * subclasses, but both need nonfair try for trylock method.

129 */

130 final boolean nonfairTryAcquire(int acquires) {

131 final Thread current = Thread.currentThread();

132 int c = getState();

133 if (c == 0) {

134 if (compareAndSetState(0, acquires)) { //CAS 方式,自旋操作

135 setExclusiveOwnerThread(current); //當(dāng)前state可用,當(dāng)前線程得到鎖了

136 return true;

137 }

138 }

139 else if (current == getExclusiveOwnerThread()) {

140 int nextc = c + acquires;

141 if (nextc < 0) // overflow

142 throw new Error("Maximum lock count exceeded");

143 setState(nextc); //鎖的值+acquires(可重入)

144 return true;

145 }

146 return false;

147 }

addWaiter

此方法用于將當(dāng)前線程加入到等待隊(duì)列的隊(duì)尾,并返回當(dāng)前線程所在的結(jié)點(diǎn)


629 /**

630 * Creates and enqueues node for current thread and given mode.

631 *

632 * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared

633 * @return the new node

634 */

635 private Node addWaiter(Node mode) {

636 Node node = new Node(mode);

637

638 for (;;) {

639 Node oldTail = tail;

640 if (oldTail != null) {

641 U.putObject(node, Node.PREV, oldTail);

642 if (compareAndSetTail(oldTail, node)) {

643 oldTail.next = node;

644 return node; //node放入隊(duì)尾并返回

645 }

646 } else {

647 initializeSyncQueue(); //這個(gè)線程和mode所創(chuàng)建的node是第一個(gè)節(jié)點(diǎn)

648 }

649 }

650 }

acquireQueued

通過(guò)tryAcquire()和addWaiter(),該線程獲取資源失敗,已經(jīng)被放入等待隊(duì)列尾部了。聰明的你立刻應(yīng)該能想到該線程下一部該干什么了吧:進(jìn)入等待狀態(tài)休息,直到其他線程徹底釋放資源后喚醒自己,自己再拿到資源,然后就可以去干自己想干的事了


881 /**

882 * Acquires in exclusive uninterruptible mode for thread already in

883 * queue. Used by condition wait methods as well as acquire.

884 *

885 * @param node the node

886 * @param arg the acquire argument

887 * @return {@code true} if interrupted while waiting

888 */

889 final boolean acquireQueued(final Node node, int arg) {

890 try {

891 boolean interrupted = false;

892 for (;;) {

893 final Node p = node.predecessor(); //node的前一個(gè)節(jié)點(diǎn)

894 if (p == head && tryAcquire(arg)) { //前一個(gè)為頭節(jié)點(diǎn)就可以嘗試拿state資源了

895 setHead(node); //拿到了,釋放頭節(jié)點(diǎn)

896 p.next = null; // help GC

897 return interrupted;

898 }

//如果自己可以休息了,就進(jìn)入waiting狀態(tài),直到被unpark()

899 if (shouldParkAfterFailedAcquire(p, node) &&

900 parkAndCheckInterrupt())

901 interrupted = true;

902 }

903 } catch (Throwable t) {

904 cancelAcquire(node);

905 throw t;

906 }

907 }

shouldParkAfterFailedAcquire


818 /**

819 * Checks and updates status for a node that failed to acquire.

820 * Returns true if thread should block. This is the main signal

821 * control in all acquire loops. Requires that pred == node.prev.

822 *

823 * @param pred node's predecessor holding status

824 * @param node the node

825 * @return {@code true} if thread should block

826 */

827 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

828 int ws = pred.waitStatus; //拿到前驅(qū)的狀態(tài)

829 if (ws == Node.SIGNAL)

830 /*

831 * This node has already set status asking a release

832 * to signal it, so it can safely park.

833 */

834 return true; //如果已經(jīng)告訴前驅(qū)拿完號(hào)后通知自己一下,那就可以安心休息了

835 if (ws > 0) {

836 /*

837 * Predecessor was cancelled. Skip over predecessors and

838 * indicate retry.

839 */

840 do {

841 node.prev = pred = pred.prev; //如果前驅(qū)放棄了,那就一直往前找,直到找到最近一個(gè)正常等待的狀態(tài),并排在它的后邊

842 } while (pred.waitStatus > 0);

843 pred.next = node;

844 } else {

845 /*

846 * waitStatus must be 0 or PROPAGATE. Indicate that we

847 * need a signal, but don't park yet. Caller will need to

848 * retry to make sure it cannot acquire before parking.

849 */

//如果前驅(qū)正常,那就把前驅(qū)的狀態(tài)設(shè)置成SIGNAL,告訴它拿完號(hào)后通知自己一下。有可能失敗,人家說(shuō)不定剛剛釋放完呢!

850 pred.compareAndSetWaitStatus(ws, Node.SIGNAL);

851 }

852 return false;

853 }

parkAndCheckInterrupt

等線程找到安全的等待位置時(shí),就可以安全進(jìn)入等待狀態(tài)(區(qū)分與阻塞的關(guān)系)


862 /**

863 * Convenience method to park and then check if interrupted.

864 *

865 * @return {@code true} if interrupted

866 */

867 private final boolean parkAndCheckInterrupt() {

868 LockSupport.park(this);

869 return Thread.interrupted();

870 }

線程狀態(tài)轉(zhuǎn)換

阻塞和等待的區(qū)別

阻塞:當(dāng)一個(gè)線程試圖獲取對(duì)象鎖(非java.util.concurrent庫(kù)中的鎖,即synchronized),而該鎖被其他線程持有,則該線程進(jìn)入阻塞狀態(tài)。它的特點(diǎn)是使用簡(jiǎn)單,由JVM調(diào)度器來(lái)決定喚醒自己,而不需要由另一個(gè)線程來(lái)顯式喚醒自己,不響應(yīng)中斷。

等待:當(dāng)一個(gè)線程等待另一個(gè)線程通知調(diào)度器一個(gè)條件時(shí),該線程進(jìn)入等待狀態(tài)。它的特點(diǎn)是需要等待另一個(gè)線程顯式地喚醒自己,實(shí)現(xiàn)靈活,語(yǔ)義更豐富,可響應(yīng)中斷。例如調(diào)用:Object.wait()、Thread.join()以及等待Lock或Condition。

線程狀態(tài).png

參考Thread詳解

AQS aquire大致流程如下:

AQS_acquire.png

release

喚醒隊(duì)列的下一個(gè)節(jié)點(diǎn)代表的線程


1274 /**

1275 * Releases in exclusive mode. Implemented by unblocking one or

1276 * more threads if {@link #tryRelease} returns true.

1277 * This method can be used to implement method {@link Lock#unlock}.

1278 *

1279 * @param arg the release argument. This value is conveyed to

1280 * {@link #tryRelease} but is otherwise uninterpreted and

1281 * can represent anything you like.

1282 * @return the value returned from {@link #tryRelease}

1283 */

1284 public final boolean release(int arg) {

1285 if (tryRelease(arg)) {

1286 Node h = head;

1287 if (h != null && h.waitStatus != 0)

1288 unparkSuccessor(h);

1289 return true;

1290 }

1291 return false;

1292 }

以上是獨(dú)占的邏輯,共享模式也是類(lèi)似的,詳細(xì)可以參考Java并發(fā)之AQS詳解,這篇文章寫(xiě)的比較清楚

線程安全

我們常見(jiàn)的保護(hù)線程安全的幾種方式為

1,使用線程安全的類(lèi)(自定義線程同步器,其實(shí)本質(zhì)還是對(duì)各種鎖的使用),2,使用鎖,3,避免使用和設(shè)置成員變量類(lèi),保持無(wú)狀態(tài)等;4,使用關(guān)鍵字保證線程安全(例:volatile)等

以 AtomicInteger為例


public class AtomicInteger extends Number implements java.io.Serializable {

private static final long serialVersionUID = 6214790243416807050L;

// setup to use Unsafe.compareAndSwapInt for updates

private static final Unsafe unsafe = Unsafe.getUnsafe();

private volatile int value;// 初始int大小

// 省略了部分代碼...

// 帶參數(shù)構(gòu)造函數(shù),可設(shè)置初始int大小

public AtomicInteger(int initialValue) {

value = initialValue;

}

// 不帶參數(shù)構(gòu)造函數(shù),初始int大小為0

public AtomicInteger() {

}

// 獲取當(dāng)前值

public final int get() {

return value;

}

// 設(shè)置值為 newValue

public final void set(int newValue) {

value = newValue;

}

//返回舊值,并設(shè)置新值為 newValue

public final int getAndSet(int newValue) {

/**

* 這里使用for循環(huán)不斷通過(guò)CAS操作來(lái)設(shè)置新值

* CAS實(shí)現(xiàn)和加鎖實(shí)現(xiàn)的關(guān)系有點(diǎn)類(lèi)似樂(lè)觀鎖和悲觀鎖的關(guān)系

* */

for (;;) {

int current = get();

if (compareAndSet(current, newValue)) //通過(guò)自旋,不斷比較當(dāng)前值和理想值,直到可以設(shè)置為止

return current;

}

}

// 原子的設(shè)置新值為update, expect為期望的當(dāng)前的值

public final boolean compareAndSet(int expect, int update) {

return unsafe.compareAndSwapInt(this, valueOffset, expect, update);

}

// 獲取當(dāng)前值current,并設(shè)置新值為current+1

public final int getAndIncrement() {

for (;;) {

int current = get();

int next = current + 1;

if (compareAndSet(current, next))

return current;

}

}

// 此處省略部分代碼,余下的代碼大致實(shí)現(xiàn)原理都是類(lèi)似的

}

本質(zhì)上這是cas(Compare and Swap),是一種樂(lè)觀鎖

CAS 悲觀鎖與樂(lè)觀鎖

Java在JDK1.5之前都是靠 synchronized關(guān)鍵字保證同步的,這種通過(guò)使用一致的鎖定協(xié)議來(lái)協(xié)調(diào)對(duì)共享狀態(tài)的訪問(wèn),可以確保無(wú)論哪個(gè)線程持有共享變量的鎖,都采用獨(dú)占的方式來(lái)訪問(wèn)這些變量。這就是一種獨(dú)占鎖,獨(dú)占鎖其實(shí)就是一種悲觀鎖,所以可以說(shuō) synchronized 是悲觀鎖。

悲觀鎖機(jī)制存在以下問(wèn)題:

1. 在多線程競(jìng)爭(zhēng)下,加鎖、釋放鎖會(huì)導(dǎo)致比較多的上下文切換和調(diào)度延時(shí),引起性能問(wèn)題。

2. 一個(gè)線程持有鎖會(huì)導(dǎo)致其它所有需要此鎖的線程掛起。

3. 如果一個(gè)優(yōu)先級(jí)高的線程等待一個(gè)優(yōu)先級(jí)低的線程釋放鎖會(huì)導(dǎo)致優(yōu)先級(jí)倒置,引起性能風(fēng)險(xiǎn)。

對(duì)比于悲觀鎖的這些問(wèn)題,另一個(gè)更加有效的鎖就是樂(lè)觀鎖。其實(shí)樂(lè)觀鎖就是:每次不加鎖而是假設(shè)沒(méi)有并發(fā)沖突而去完成某項(xiàng)操作,如果因?yàn)椴l(fā)沖突失敗就重試,直到成功為止。

CAS是樂(lè)觀鎖技術(shù),當(dāng)多個(gè)線程嘗試使用CAS同時(shí)更新同一個(gè)變量時(shí),只有其中一個(gè)線程能更新變量的值,而其它線程都失敗,失敗的線程并不會(huì)被掛起,而是被告知這次競(jìng)爭(zhēng)中失敗,并可以再次嘗試

樂(lè)觀鎖的缺點(diǎn)

1 ABA 問(wèn)題

如果一個(gè)變量V初次讀取的時(shí)候是A值,并且在準(zhǔn)備賦值的時(shí)候檢查到它仍然是A值,那我們就能說(shuō)明它的值沒(méi)有被其他線程修改過(guò)了嗎?很明顯是不能的,因?yàn)樵谶@段時(shí)間它的值可能被改為其他值,然后又改回A,那CAS操作就會(huì)誤認(rèn)為它從來(lái)沒(méi)有被修改過(guò)。這個(gè)問(wèn)題被稱為CAS操作的 “ABA”問(wèn)題。(比如說(shuō)一個(gè)線程one從內(nèi)存位置V中取出A,這時(shí)候另一個(gè)線程two也從內(nèi)存中取出A,并且two進(jìn)行了一些操作變成了B,然后two又將V位置的數(shù)據(jù)變成A,這時(shí)候線程one進(jìn)行CAS操作發(fā)現(xiàn)內(nèi)存中仍然是A,然后one操作成功。盡管線程one的CAS操作成功,但是不代表這個(gè)過(guò)程就是沒(méi)有問(wèn)題的)

JDK 1.5 以后的 AtomicStampedReference 類(lèi)就提供了此種能力(加一個(gè)時(shí)間戳),其中的 compareAndSet 方法就是首先檢查當(dāng)前引用是否等于預(yù)期引用,并且當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志,如果全部相等,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值。

2 循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)大

自旋CAS(也就是不成功就一直循環(huán)執(zhí)行直到成功)如果長(zhǎng)時(shí)間不成功,會(huì)給CPU帶來(lái)非常大的執(zhí)行開(kāi)銷(xiāo)。 如果JVM能支持處理器提供的pause指令那么效率會(huì)有一定的提升,pause指令有兩個(gè)作用,第一它可以延遲流水線執(zhí)行指令(de-pipeline),使CPU不會(huì)消耗過(guò)多的執(zhí)行資源,延遲的時(shí)間取決于具體實(shí)現(xiàn)的版本,在一些處理器上延遲時(shí)間是零。第二它可以避免在退出循環(huán)的時(shí)候因內(nèi)存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush),從而提高CPU的執(zhí)行效率。

3 只能保證一個(gè)共享變量的原子操作

CAS 只對(duì)單個(gè)共享變量有效,當(dāng)操作涉及跨多個(gè)共享變量時(shí) CAS 無(wú)效。但是從 JDK 1.5開(kāi)始,提供了AtomicReference類(lèi)來(lái)保證引用對(duì)象之間的原子性,你可以把多個(gè)變量放在一個(gè)對(duì)象里來(lái)進(jìn)行 CAS 操作.所以我們可以使用鎖或者利用AtomicReference類(lèi)把多個(gè)共享變量合并成一個(gè)共享變量來(lái)操作

總結(jié)

綜上,我們大致了解了java多線程同步,線程安全的一些注意事項(xiàng);了解aqs(同步器)和cas(樂(lè)觀鎖技術(shù)-原子類(lèi));這些都被用來(lái)定義concurrent包中的基礎(chǔ)類(lèi);而concurrent包高層類(lèi)正是基礎(chǔ)類(lèi)的進(jìn)一步復(fù)用

擴(kuò)展學(xué)習(xí)-concurrent高層類(lèi),如# ConcurrentHashMap

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容