最開始接觸到相關的內容應該是從volatile關鍵字開始的吧,知道它可以保證變量的可見性,而且利用它可以實現讀與寫的原子操作。。。但是要實現一些復合的操作volatile就無能為力了。。。最典型的代表是遞增和遞減的操作。。。。
我們提供的服務有:成都網站設計、做網站、微信公眾號開發(fā)、網站優(yōu)化、網站認證、南漳ssl等。為近1000家企事業(yè)單位解決了網站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術的南漳網站制作公司
我們知道,在并發(fā)的環(huán)境下,要實現數據的一致性,最簡單的方式就是加鎖,保證同一時刻只有一個線程可以對數據進行操作。。。。例如一個計數器,我們可以用如下的方式來實現:
public class Counter { private volatile int a = 0; public synchronized int incrAndGet(int number) { this.a += number; return a; } public synchronized int get() { return a; } }
我們對操作都用synchronized關鍵字進行修飾,保證對屬性a的同步訪問。。。這樣子確實可以保證在并發(fā)環(huán)境下a的一致性,但是由于使用了鎖,鎖的開銷,線程的調度等等會使得程序的伸縮性受到了限制,于是就有了很多無鎖的實現方式。。。。
其實這些無鎖的方法都利用了處理器所提供的一些CAS(compare and switch)指令,這個CAS到底干了啥事情呢,可以用下面這個方法來說明CAS所代表的語義:
public synchronized int compareAndSwap(int expect, int newValue) { int old = this.a; if (old == expect) { this.a = newValue; } return old; }
好吧,通過代碼應該對CAS語義的標書很清楚了吧,好像現在大多數的處理器都實現了原子的CAS指令了吧。。
好啦,那么接下來來看看在java中CAS都用在了什么地方了吧,首先來看AtomicInteger類型吧,這個是并發(fā)庫里面提供的一個類型:
private volatile int value;
這個是內部定義的一個屬性吧,用于保存值,由于是volatile類型的,所以可以保證線程之間的可見性以及讀寫的原子性。。。
那么接下來來看看幾個比較常用的方法:
public final int addAndGet(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return next; } }
這個方法的作用是在當前值的基礎上加上delta,這里可以看到整個方法中并沒有加鎖,這代碼其實就算是java中實現無鎖計數器的方法,這里compareAndSet方法的定義如下:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
由于調用了unsafe的方法,所以這個就無能為力了,其實應該能猜到JVM調用了處理器本身的CAS指令來實現原子的操作。。。
基本上AtomicInteger類型的重要方法都是采用無鎖的方式實現的。。因此在并發(fā)環(huán)境下,用這種類型能有更好的性能。。。
上面算是搞定了在java中實現無鎖的計數器,接下來來看看如何實現無鎖棧,直接貼代碼了,代碼是從《JAVA并發(fā)編程實戰(zhàn)》中模仿下來的:
package concurrenttest; import java.util.concurrent.atomic.AtomicReference; public class ConcurrentStack<e> { AtomicReference<node<e>> top = new AtomicReference<node<e>>(); public void push(E item) { Node<e> newHead = new Node<e>(item); Node<e> oldHead; while (true) { oldHead = top.get(); newHead.next = oldHead; if (top.compareAndSet(oldHead, newHead)) { return; } } } public E pop() { while (true) { Node<e> oldHead = top.get(); if (oldHead == null) { return null; } Node<e> newHead = oldHead.next; if (top.compareAndSet(oldHead, newHead)) { return oldHead.item; } } } private static class Node<e> { public final E item; public Node<e> next; public Node(E item) { this.item = item; } } }
好啦,上面的代碼就算是實現了一個無鎖的棧,簡單吧。。。在并發(fā)環(huán)境中,無鎖的數據結構伸縮性能夠比用鎖好得多。。。
在提到無鎖編程的時候,就不得不提到無鎖隊列,其實在concurrent庫中已經提供了無鎖隊列的實現:ConcurrentLinkedQueue,我們來看看它的重要的方法實現吧:
public boolean offer(E e) { checkNotNull(e); final Node<e> newNode = new Node<e>(e); for (Node<e> t = tail, p = t;;) { Node<e> q = p.next; if (q == null) { // p is last node if (p.casNext(null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this queue, // and for newNode to become "live". if (p != t) // hop two nodes at a time casTail(t, newNode); // Failure is OK. return true; } // Lost CAS race to another thread; re-read next } else if (p == q) // We have fallen off list. If tail is unchanged, it // will also be off-list, in which case we need to // jump to head, from which all live nodes are always // reachable. Else the new tail is a better bet. p = (t != (t = tail)) ? t : head; else // Check for tail updates after two hops. p = (p != t && t != (t = tail)) ? t : q; } }
這個方法用于在隊列的尾部添加元素,這里可以看到沒有加鎖,對于具體的無鎖算法,采用的是Michael-Scott提出的非阻塞鏈表鏈接算法。。。具體是怎么樣子的,可以到《JAVA并發(fā)編程實戰(zhàn)》中去看吧,有比較詳細的介紹。
另外對于其他方法,其實都是采用無鎖的方式實現的。
最后,在實際的編程中,在并發(fā)環(huán)境中最好還是采用這些無鎖的實現,畢竟它的伸縮性更好。
總結
以上是本文關于Java語言中cas指令的無鎖編程實現實例的全部介紹,希望對大家有所幫助!
標題名稱:Java語言中cas指令的無鎖編程實現實例
標題鏈接:http://www.rwnh.cn/article2/psgcic.html
成都網站建設公司_創(chuàng)新互聯,為您提供網站導航、網站策劃、Google、App設計、商城網站、用戶體驗
聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯