程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java並發掌握機制詳解

Java並發掌握機制詳解

編輯:關於JAVA

Java並發掌握機制詳解。本站提示廣大學習愛好者:(Java並發掌握機制詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是Java並發掌握機制詳解正文


在普通性開辟中,筆者常常看到許多同窗在看待java並發開辟模子中只會應用一些基本的辦法。好比Volatile,synchronized。像Lock和atomic這類高等並發包許多人其實不常常應用。我想年夜部門緣由都是來之於對道理的不屬性招致的。在忙碌的開辟任務中,又有誰會很精確的掌握和應用准確的並發模子呢?

所以比來基於這個思惟,自己盤算把並發掌握機制這部門整頓成一篇文章。既是對本身控制常識的一個回想,也是願望這篇講到的類容能贊助到年夜部門開辟者。 

並行法式開辟弗成防止地要觸及多線程、多義務的協作和數據同享等成績。在JDK中,供給了多種門路完成多線程間的並發掌握。好比經常使用的:外部鎖、重入鎖、讀寫鎖和旌旗燈號量。 

Java內存模子 

在java中,每個線程有一塊任務內存區,個中寄存著被一切線程同享的主內存中的變量的值的拷貝。當線程履行時,它在本身的任務內存中操作這些變量。 

為了存取一個同享的變量,一個線程平日先獲得鎖定而且消除它的任務內存區,這包管該同享變量從一切線程的同享內存區准確地裝入到線程的任務內存區,當線程解鎖時包管該任務內存區中變量的值協會到同享內存中。 

當一個線程應用某一個變量時,豈論法式能否准確地應用線程同步操作,它獲得的值必定是由它自己或許其他線程存儲到變量中的值。例如,假如兩個線程把分歧的值或許對象援用存儲到統一個同享變量中,那末該變量的值要末是這個線程的,要末是誰人線程的,同享變量的值不會是由兩個線程的援用值組合而成。

一個變量時Java法式可以存取的一個地址,它不只包含根本類型變量、援用類型變量,並且還包含數組類型變量。保留在主內存區的變量可以被一切線程同享,然則一個線程存取另外一個線程的參數或許部分變量時弗成能的,所以開辟人員不用擔憂部分變量的線程平安成績。 

volatile變量–多線程間可見 

因為每一個線程都有本身的任務內存區,是以當一個線程轉變本身的任務內存中的數據時,對其他線程來講,能夠是弗成見的。為此,可使用volatile症結字破事一切線程軍讀寫內存中的變量,從而使得volatile變量在多線程間可見。 

聲明為volatile的變量可以做到以下包管: 

1、其他線程對變量的修正,可和時反響在以後線程中;
2、確保以後線程對volatile變量的修正,能實時寫回到同享內存中,並被其他線程所見;
3、應用volatile聲明的變量,編譯器會包管其有序性。 

同步症結字synchronized 

同步症結字synchronized是Java說話中最為經常使用的同步辦法之一。在JDK晚期版本中,synchronized的機能其實不是太好,值合適於鎖競爭不是特殊劇烈的場所。在JDK6中,synchronized和非公正鎖的差距曾經減少。更加主要的是,synchronized更加簡練清楚明了,代碼可讀性和保護性比擬好。 

鎖定一個對象的辦法:
 public synchronized void method(){}
當method()辦法被挪用時,挪用線程起首必需取得以後對象所,若以後對象鎖被其他線程持有,這挪用線程會期待,犯罪停止後,對象鎖會被釋放,以上辦法等價於上面的寫法:

 public void method(){
synchronized(this){
// do something …
}
} 

其次,應用synchronized還可以結構同步塊,與同步辦法比擬,同步塊可以更加准確掌握同步代碼規模。一個小的同步代碼異常有離與鎖的快進快出,從而使體系具有更高的吞吐量。

 public void method(Object o){
// before
synchronized(o){
// do something ...
}
// after
} 

synchronized也能夠用於static函數:
public synchronized static void method(){}
這個處所必定要留意,synchronized的鎖是加在以後Class對象上,是以,一切對該辦法的挪用,都必需取得Class對象的鎖。 

固然synchronized可以包管對象或許代碼段的線程平安,然則僅應用synchronized照樣缺乏以掌握具有龐雜邏輯的線程交互。為了完成多線程間的交互,還須要應用Object對象的wait()和notify()辦法。 

典范用法:

 synchronized(obj){
  while(<?>){
    obj.wait();
    // 收到告訴後,持續履行。
  }
} 

在應用wait()辦法前,須要取得對象鎖。在wait()辦法履行時,以後線程或釋放obj的獨有鎖,供其他線程應用。 

當期待在obj上線程收到obj.notify()時,它就可以從新取得obj的獨有鎖,並持續運轉。留意了,notify()辦法是隨機喚起期待在以後對象的某一個線程。

上面是一個壅塞隊列的完成:

 public class BlockQueue{
 private List list = new ArrayList();

 public synchronized Object pop() throws InterruptedException{
 while (list.size()==0){
 this.wait();
 }
 if (list.size()>0){
 return list.remove(0);
 } else{
 return null;
 }
 }

 public synchronized Object put(Object obj){
 list.add(obj);
 this.notify();
 }

} 

synchronized合營wait()、notify()應當是Java開辟者必需控制的根本技巧。 

Reentrantlock重入鎖 

Reentrantlock稱為重入鎖。它比synchronized具有加倍壯大的功效,它可以中止、可准時。在高並發的情形下,它比synchronized有顯著的機能優勢。 

Reentrantlock供給了公正和非公正兩種鎖。公正鎖是對鎖的獲得是先輩先出,而非公正鎖是可以插隊的。固然從機能上剖析,非公正鎖的機能要好很多。是以,在無特別須要,應當優選非公正鎖,然則synchronized供給鎖業不是相對公正的。Reentrantlock在結構的時刻可以指定鎖能否公正。 

在應用重入鎖時,必定要在法式最初釋放鎖。普通釋放鎖的代碼要寫在finally裡。不然,假如法式湧現異常,Loack就永久沒法釋放了。synchronized的鎖是JVM最初主動釋放的。

經典應用方法以下:

 try {
 if (lock.tryLock(5, TimeUnit.SECONDS)) { //假如曾經被lock,測驗考試期待5s,看能否可以取得鎖,假如5s後依然沒法取得鎖則前往false持續履行
 // lock.lockInterruptibly();可以呼應中止事宜
 try { 
 //操作
 } finally {
 lock.unlock();
 }
 }
} catch (InterruptedException e) {
 e.printStackTrace(); //以後線程被中止時(interrupt),會拋InterruptedException 
} 

Reentrantlock供給了異常豐碩的鎖掌握功效,靈巧運用這些掌握辦法,可以進步運用法式的機能。不外這裡並不是是死力推舉應用Reentrantlock。重入鎖算是JDK中供給的高等開辟對象。 

ReadWriteLock讀寫鎖
讀寫分別是一種異常罕見的數據處置思惟。在sql中應當算是必需用到的技巧。ReadWriteLock是在JDK5中供給的讀寫分別鎖。讀寫分別鎖可以有用地贊助削減鎖競爭,以晉升體系機能。讀寫分別應用場景重要是假如在體系中,讀操作次數遠弘遠於寫操作。應用方法以下:

 private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock();
private Lock writeLock = readWriteLock.writeLock();
public Object handleRead() throws InterruptedException {
  try {
    readLock.lock();
    Thread.sleep(1000);
    return value;
  }finally{
    readLock.unlock();
  }
}
public Object handleRead() throws InterruptedException {
  try {
    writeLock.lock();
    Thread.sleep(1000);
    return value;
  }finally{
    writeLock.unlock();
  }
} 

Condition對象
 Conditiond對象用於調和多線程間的龐雜協作。重要與鎖相干聯。經由過程Lock接口中的newCondition()辦法可以生成一個與Lock綁定的Condition實例。Condition對象和鎖的關系就如用Object.wait()、Object.notify()兩個函數和synchronized症結字一樣。 
這裡可以把ArrayBlockingQueue的源碼摘出來看一下:

 public class ArrayBlockingQueue extends AbstractQueue
implements BlockingQueue, java.io.Serializable {
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;

public ArrayBlockingQueue(int capacity, boolean fair) {
  if (capacity <= 0)
    throw new IllegalArgumentException();
  this.items = new Object[capacity];
  lock = new ReentrantLock(fair); 
  notEmpty = lock.newCondition(); // 生成與Lock綁定的Condition
  notFull = lock.newCondition();
}

public void put(E e) throws InterruptedException {
  checkNotNull(e);
  final ReentrantLock lock = this.lock;
  lock.lockInterruptibly();
  try {
    while (count == items.length)
      notFull.await();
    insert(e);
  } finally {
    lock.unlock();
  }
}

private void insert(E x) {
  items[putIndex] = x;
  putIndex = inc(putIndex);
  ++count;
  notEmpty.signal(); // 告訴
}

public E take() throws InterruptedException {
  final ReentrantLock lock = this.lock;
  lock.lockInterruptibly();
  try {
    while (count == 0) // 假如隊列為空
      notEmpty.await(); // 則花費者隊列要期待一個非空的旌旗燈號
    return extract();
  } finally {
    lock.unlock();
  }
}

private E extract() {
  final Object[] items = this.items;
  E x = this.<E>cast(items[takeIndex]);
  items[takeIndex] = null;
  takeIndex = inc(takeIndex);
  --count;
  notFull.signal(); // 告訴put() 線程隊列已有余暇空間
  return x;
}

// other code
} 

Semaphore旌旗燈號量 
旌旗燈號量為多線程協作供給了更加壯大的掌握辦法。旌旗燈號量是對鎖的擴大。不管是外部鎖synchronized照樣重入鎖ReentrantLock,一次都許可一個線程拜訪一個資本,而旌旗燈號量卻可以指定多個線程同時拜訪某一個資本。從結構函數可以看出:
 public Semaphore(int permits) {}
public Semaphore(int permits, boolean fair){} // 可以指定能否公正
permits指定了旌旗燈號量的准入書,也就是同時能請求若干個允許。當每一個線程每次只請求一個允許時,這就相當於指定了同時有若干個線程可以拜訪某一個資本。這裡枚舉一下重要辦法的應用:
  public void acquire() throws InterruptedException {} //測驗考試取得一個准入的允許。若沒法取得,則線程會期待,曉得有線程釋放一個允許或許以後線程被中止。
 public void acquireUninterruptibly(){} // 相似於acquire(),然則不會呼應中止。
 public boolean tryAcquire(){} // 測驗考試獲得,假如勝利則為true,不然false。這個辦法不會期待,立刻前往。
 public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {} // 測驗考試期待多長時光
 public void release() //用於在現場拜訪資本停止後,釋放一個允許,以使其他期待允許的線程可以停止資本拜訪。 

上面來看一下JDK文檔中供給應用旌旗燈號量的實例。這個實例很好的說明了若何經由過程旌旗燈號量掌握資本拜訪。

 public class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
public Object getItem() throws InterruptedException {
  available.acquire();
  // 請求一個允許
  // 同時只能有100個線程進入獲得可用項,
  // 跨越100個則須要期待
  return getNextAvailableItem();
}

public void putItem(Object x) {
  // 將給定項放回池內,標志為未被應用
  if (markAsUnused(x)) {
    available.release();
    // 新增了一個可用項,釋放一個允許,要求資本的線程被激活一個
  }
}

// 僅作示例參考,非真實數據
protected Object[] items = new Object[MAX_AVAILABLE]; // 用於對象池復用對象
protected boolean[] used = new boolean[MAX_AVAILABLE]; // 標志感化

protected synchronized Object getNextAvailableItem() {
  for (int i = 0; i < MAX_AVAILABLE; ++i) {
    if (!used[i]) {
      used[i] = true;
      return items[i];
    }
  }
  return null;
}

protected synchronized boolean markAsUnused(Object item) {
  for (int i = 0; i < MAX_AVAILABLE; ++i) {
    if (item == items[i]) {
      if (used[i]) {
        used[i] = false;
        return true;
      } else {
        return false;
      }
    }
  }
  return false;
}
} 

此實例簡略完成了一個對象池,對象池最年夜容量為100。是以,當同時有100個對象要求時,對象池就會湧現資本缺乏,未能取得資本的線程就須要期待。當某個線程應用對象終了後,就須要將對象前往給對象池。此時,因為可用資本增長,是以,可以激活一個期待該資本的線程。 

ThreadLocal線程部分變量 
在剛開端接觸ThreadLocal,筆者很難懂得這個線程部分變量的應用場景。當如今回過火去看,ThreadLocal是一種多線程間並發拜訪變量的處理計劃。與synchronized等加鎖的方法分歧,ThreadLocal完整不供給鎖,而應用了以空間換時光的手腕,為每一個線程供給變量的自力正本,以保證線程平安,是以它不是一種數據同享的處理計劃。 

ThreadLocal是處理線程平安成績一個很好的思緒,ThreadLocal類中有一個Map,用於存儲每個線程的變量正本,Map中元素的鍵為線程對象,而值對應線程的變量正本,因為Key值弗成反復,每個“線程對象”對應線程的“變量正本”,而達到了線程平安。 

特殊值得留意的處所,從機能上說,ThreadLocal其實不具有相對的又是,在並發量不是很高時,也行加鎖的機能會更好。但作為一套與鎖完整有關的線程平安處理計劃,在高並發量或許所競爭劇烈的場所,應用ThreadLocal可以在必定水平上削減鎖競爭。 

上面是一個ThreadLocal的簡略應用:

 public class TestNum {
 // 經由過程匿名外部類籠罩ThreadLocal的initialValue()辦法,指定初始值
 private static ThreadLocal seqNum = new ThreadLocal() {
 public Integer initialValue() {
 return 0;
 }
 };
 // 獲得下一個序列值
 public int getNextNum() {
 seqNum.set(seqNum.get() + 1);
 return seqNum.get();
}public static void main(String[] args) {
 TestNum sn = new TestNum();
 //3個線程同享sn,各自發生序列號
 TestClient t1 = new TestClient(sn);
 TestClient t2 = new TestClient(sn);
 TestClient t3 = new TestClient(sn);
 t1.start();
 t2.start();
 t3.start();
 }
private static class TestClient extends Thread {
 private TestNum sn;
public TestClient(TestNum sn) {
 this.sn = sn;
 }
public void run() {
 for (int i = 0; i < 3; i++) {
 // 每一個線程打出3個序列值
 System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn["
 + sn.getNextNum() + "]");
 }
 }
 }
 } 

輸入成果:
 thread[Thread-0] –> sn[1]
thread[Thread-1] –> sn[1]
thread[Thread-2] –> sn[1]
thread[Thread-1] –> sn[2]
thread[Thread-0] –> sn[2]
thread[Thread-1] –> sn[3]
thread[Thread-2] –> sn[2]
thread[Thread-0] –> sn[3]
thread[Thread-2] –> sn[3]
輸入的成果信息可以發明每一個線程所發生的序號固然都同享統一個TestNum實例,但它們並沒有產生互相攪擾的情形,而是各自發生自力的序列號,這是由於ThreadLocal為每個線程供給了零丁的正本。 

鎖的機能和優化 
“鎖”是最經常使用的同步辦法之一。在平凡開辟中,常常能看到許多同窗直接把鎖加很年夜一段代碼上。還有的同窗只會用一種鎖方法處理一切同享成績。明顯如許的編碼是讓人沒法接收的。特殊的在高並發的情況下,劇烈的鎖競爭會招致法式的機能降低德加倍顯著。是以公道應用鎖對法式的機能直接相干。 

1、線程的開支 
在多核情形下,應用多線程可以顯著進步體系的機能。然則在現實情形中,應用多線程的方法會額定增長體系的開支。絕對於單核體系義務自己的資本消費外,多線程運用還須要保護額定多線程獨有的信息。好比,線程自己的元數據,線程調劑,線程高低文的切換等。 

2、減小鎖持有時光 
在應用鎖停止並發掌握的法式中,當鎖產生競爭時,單個線程對鎖的持有時光與體系機能有著直接的關系。假如線程持有鎖的時光很長,那末絕對地,鎖的競爭水平也就越劇烈。是以,在法式開辟進程中,應當盡量地削減對某個鎖的占領時光,以削減線程間互斥的能夠。好比上面這一段代碼:

 public synchronized void syncMehod(){
beforeMethod();
mutexMethod();
afterMethod();
} 

此實例假如只要mutexMethod()辦法是有同步須要的,而在beforeMethod(),和afterMethod()其實不須要做同步掌握。假如beforeMethod(),和afterMethod()分離是分量級的辦法,則會消費較長的CPU時光。在這個時刻,假如並發量較年夜時,應用這類同步計劃會招致期待線程年夜量增長。由於以後履行的線程只要在履行完一切義務後,才會釋放鎖。 

上面是優化後的計劃,只在需要的時刻停止同步,如許就可以顯著削減線程持有鎖的時光,進步體系的吞吐量。代碼以下:

 public void syncMehod(){
beforeMethod();
synchronized(this){
mutexMethod();
}
afterMethod();
} 

3、削減鎖粒度 

減小鎖粒度也是一種減弱多線程鎖競爭的一種有用手腕,這類技巧典范的應用場景就是ConcurrentHashMap這個類。在通俗的HashMap中每當對聚集停止add()操作或許get()操作時,老是取得聚集對象的鎖。這類操作完整是一種同步行動,由於鎖是在全部聚集對象上的,是以,在高並發時,劇烈的鎖競爭會影響到體系的吞吐量。 

假如看過源碼的同窗應當曉得HashMap是數組+鏈表的方法做完成的。ConcurrentHashMap在HashMap的基本大將全部HashMap分紅若干個段(Segment),每一個段都是一個子HashMap。假如須要在增長一個新的表項,其實不是將這個HashMap加鎖,二十搜線依據hashcode獲得該表項應當被寄存在哪一個段中,然後對該段加鎖,並完成put()操作。如許,在多線程情況中,假如多個線程同時停止寫入操作,只需被寫入的項不存在統一個段中,那末線程間即可以做到真實的並行。詳細的完成願望讀者本身花點時光讀一讀ConcurrentHashMap這個類的源碼,這裡就不再做過量描寫了。 

4、鎖分別  
在後面提起過ReadWriteLock讀寫鎖,那末讀寫分別的延長就是鎖的分別。異樣可以在JDK中找到鎖分別的源碼LinkedBlockingQueue。
 

public class LinkedBlockingQueue extends AbstractQueue
implements BlockingQueue, java.io.Serializable {
/* Lock held by take, poll, etc /
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();

/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();

/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();

public E take() throws InterruptedException {
  E x;
  int c = -1;
  final AtomicInteger count = this.count;
  final ReentrantLock takeLock = this.takeLock;
  takeLock.lockInterruptibly(); // 不克不及有兩個線程同時讀取數據
  try {
    while (count.get() == 0) { // 假如以後沒有可用數據,一向期待put()的告訴
      notEmpty.await();
    }
    x = dequeue(); // 從頭部移除一項
    c = count.getAndDecrement(); // size減1
    if (c > 1)
      notEmpty.signal(); // 告訴其他take()操作
  } finally {
    takeLock.unlock(); // 釋放鎖
  }
  if (c == capacity)
    signalNotFull(); // 告訴put()操作,已有空余空間
  return x;
}

public void put(E e) throws InterruptedException {
  if (e == null) throw new NullPointerException();
  // Note: convention in all put/take/etc is to preset local var
  // holding count negative to indicate failure unless set.
  int c = -1;
  Node<E> node = new Node(e);
  final ReentrantLock putLock = this.putLock;
  final AtomicInteger count = this.count;
  putLock.lockInterruptibly(); // 不克不及有兩個線程同時put數據
  try {
    /*
     * Note that count is used in wait guard even though it is
     * not protected by lock. This works because count can
     * only decrease at this point (all other puts are shut
     * out by lock), and we (or some other waiting put) are
     * signalled if it ever changes from capacity. Similarly
     * for all other uses of count in other wait guards.
     */
    while (count.get() == capacity) { // 隊列滿了 則期待
      notFull.await();
    }
    enqueue(node); // 參加隊列
    c = count.getAndIncrement();// size加1
    if (c + 1 < capacity)
      notFull.signal(); // 假如有足夠空間,告訴其他線程
  } finally {
    putLock.unlock();// 釋放鎖
  }
  if (c == 0)
    signalNotEmpty();// 拔出勝利後,告訴take()操作讀取數據
}

// other code   
} 

這裡須要解釋一下的就是,take()和put()函數是互相自力的,它們之間不存在鎖競爭關系。只須要在take()和put()各自辦法外部分離對takeLock和putLock產生競爭。從而,減弱了鎖競爭的能夠性。 

5、鎖粗化 
下面說到的減小鎖時光和粒度,如許做就是為了知足每一個線程持有鎖的時光盡可能短。然則,在粒度上應當掌握一個度,假如對用一個鎖一直地停止要求、同步和釋放,其自己也會消費體系名貴的資本,反而加年夜了體系開支。 
我們須要曉得的是,虛擬機在碰到連續串聯續的對統一鎖赓續停止要求和釋放的操作時,便會把一切的鎖操作整分解對鎖的一次要求,從而削減對鎖的要求同步次數,如許的操作叫做鎖的粗化。上面是一段整合實例演示:

 public void syncMehod(){
synchronized(lock){
method1();
}
synchronized(lock){
method2();
}
} 
JVM整合後的情勢:
 public void syncMehod(){
synchronized(lock){
method1();
method2();
}
} 

是以,如許的整合給我們開辟人員對鎖粒度的掌握給出了很好的演示感化。 

無鎖的並行盤算 
下面花了很年夜篇幅在說鎖的工作,同時也提到過鎖是會帶來必定的高低文切換的額定資本開支,在高並發時,”鎖“的劇烈競爭能夠會成為體系瓶頸。是以,這裡可使用一種非壅塞同步辦法。這類無鎖方法仍然能包管數據和法式在高並發情況下堅持多線程間的分歧性。 
1、非壅塞同步/無鎖
 非壅塞同步方法其其實後面的ThreadLocal中曾經有所表現,每一個線程具有各自自力的變量正本,是以在並行盤算時,無需互相期待。這裡筆者重要推舉一種更加主要的、基於比擬並交流(Compare And Swap)CAS算法的無鎖並發掌握辦法。 

CAS算法的進程:它包括3個參數CAS(V,E,N)。V表現要更新的變量,E表現預期值,N表現新值。僅當V值等於E值時,才會將V的值設為N,假如V值和E值分歧,則解釋曾經有其他線程做了更新,則以後線程甚麼都不做。最初CAS前往以後V的真實值。CAS操作時抱著悲觀的立場停止的,它老是以為本身可以勝利完成操作。當多個線程同時應用CAS操作一個變量時,只要一個會勝出,並勝利更新,其他俊輝掉敗。掉敗的線程不會被掛起,僅是原告知掉敗,而且許可再次測驗考試,固然也許可掉敗的線程廢棄操作。基於如許的道理,CAS操作實時沒有鎖,也能夠發明其他線程對以後線程的攪擾,而且停止適當的處置。 

2、原子量操作
JDK的java.util.concurrent.atomic包供給了應用無鎖算法完成的原子操作類,代碼外部重要應用了底層native代碼的完成。有興致的同窗可以持續跟蹤一下native層面的代碼。這裡就不貼表層的代碼完成了。 

上面重要以一個例子來展現通俗同步辦法和無鎖同步的機能差距:

public class TestAtomic {
private static final int MAX_THREADS = 3;
private static final int TASK_COUNT = 3;
private static final int TARGET_COUNT = 100 * 10000;
private AtomicInteger acount = new AtomicInteger(0);
private int count = 0;
synchronized int inc() {
  return ++count;
}

synchronized int getCount() {
  return count;
}

public class SyncThread implements Runnable {
  String name;
  long startTime;
  TestAtomic out;

  public SyncThread(TestAtomic o, long startTime) {
    this.out = o;
    this.startTime = startTime;
  }

  @Override
  public void run() {
    int v = out.inc();
    while (v < TARGET_COUNT) {
      v = out.inc();
    }
    long endTime = System.currentTimeMillis();
    System.out.println("SyncThread spend:" + (endTime - startTime) + "ms" + ", v=" + v);
  }
}

public class AtomicThread implements Runnable {
  String name;
  long startTime;

  public AtomicThread(long startTime) {
    this.startTime = startTime;
  }

  @Override
  public void run() {
    int v = acount.incrementAndGet();
    while (v < TARGET_COUNT) {
      v = acount.incrementAndGet();
    }
    long endTime = System.currentTimeMillis();
    System.out.println("AtomicThread spend:" + (endTime - startTime) + "ms" + ", v=" + v);
  }
}

@Test
public void testSync() throws InterruptedException {
  ExecutorService exe = Executors.newFixedThreadPool(MAX_THREADS);
  long startTime = System.currentTimeMillis();
  SyncThread sync = new SyncThread(this, startTime);
  for (int i = 0; i < TASK_COUNT; i++) {
    exe.submit(sync);
  }
  Thread.sleep(10000);
}

@Test
public void testAtomic() throws InterruptedException {
  ExecutorService exe = Executors.newFixedThreadPool(MAX_THREADS);
  long startTime = System.currentTimeMillis();
  AtomicThread atomic = new AtomicThread(startTime);
  for (int i = 0; i < TASK_COUNT; i++) {
    exe.submit(atomic);
  }
  Thread.sleep(10000);
}
} 

測試成果以下:
 testSync():
SyncThread spend:201ms, v=1000002
SyncThread spend:201ms, v=1000000
SyncThread spend:201ms, v=1000001
testAtomic():
AtomicThread spend:43ms, v=1000000
AtomicThread spend:44ms, v=1000001
AtomicThread spend:46ms, v=1000002
信任如許的測試成果將外部鎖和非壅塞同步算法的機能差別表現的異常顯著。是以筆者更推舉直接視同atomic下的這個原子類。 

停止語 
終究把想表達的這些器械整頓完成了,其實還有一些想CountDownLatch如許的類沒有講到。不外下面的所講到的相對是並發編程中的焦點。或許有些讀者同伙能在網上看到許多如許的常識點,然則小我照樣認為常識只要在比較的基本上能力找到它適合的應用場景。是以,這也是小編整頓這篇文章的緣由,也願望這篇文章能幫到更多的同窗。

以上就是本文的全體內容,願望對年夜家的進修有所贊助,也願望年夜家多多支撐。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved