日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人文社科 > 生活经验 >内容正文

生活经验

【java线程】锁机制:synchronized、Lock、Condition

發(fā)布時(shí)間:2023/11/27 生活经验 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【java线程】锁机制:synchronized、Lock、Condition 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

【Java線(xiàn)程】鎖機(jī)制:synchronized、Lock、Condition
原創(chuàng) 2013年08月14日 17:15:55 標(biāo)簽:Java /多線(xiàn)程 74967
http://www.infoq.com/cn/articles/java-memory-model-5 深入理解Java內(nèi)存模型(五)——鎖
http://www.ibm.com/developerworks/cn/java/j-jtp10264/ Java 理論與實(shí)踐: JDK 5.0 中更靈活、更具可伸縮性的鎖定機(jī)制
http://blog.csdn.net/ghsau/article/details/7481142
1、synchronized

把代碼塊聲明為 synchronized,有兩個(gè)重要后果,通常是指該代碼具有 原子性(atomicity)和 可見(jiàn)性(visibility)。
1.1 原子性

原子性意味著個(gè)時(shí)刻,只有一個(gè)線(xiàn)程能夠執(zhí)行一段代碼,這段代碼通過(guò)一個(gè)monitor object保護(hù)。從而防止多個(gè)線(xiàn)程在更新共享狀態(tài)時(shí)相互沖突。

1.2 可見(jiàn)性

可見(jiàn)性則更為微妙,它要對(duì)付內(nèi)存緩存和編譯器優(yōu)化的各種反常行為。它必須確保釋放鎖之前對(duì)共享數(shù)據(jù)做出的更改對(duì)于隨后獲得該鎖的另一個(gè)線(xiàn)程是可見(jiàn)的 。
作用:如果沒(méi)有同步機(jī)制提供的這種可見(jiàn)性保證,線(xiàn)程看到的共享變量可能是修改前的值或不一致的值,這將引發(fā)許多嚴(yán)重問(wèn)題。
原理:當(dāng)對(duì)象獲取鎖時(shí),它首先使自己的高速緩存無(wú)效,這樣就可以保證直接從主內(nèi)存中裝入變量。 同樣,在對(duì)象釋放鎖之前,它會(huì)刷新其高速緩存,強(qiáng)制使已做的任何更改都出現(xiàn)在主內(nèi)存中。 這樣,會(huì)保證在同一個(gè)鎖上同步的兩個(gè)線(xiàn)程看到在 synchronized 塊內(nèi)修改的變量的相同值。

一般來(lái)說(shuō),線(xiàn)程以某種不必讓其他線(xiàn)程立即可以看到的方式(不管這些線(xiàn)程在寄存器中、在處理器特定的緩存中,還是通過(guò)指令重排或者其他編譯器優(yōu)化),不受緩存變量值的約束,但是如果開(kāi)發(fā)人員使用了同步,那么運(yùn)行庫(kù)將確保某一線(xiàn)程對(duì)變量所做的更新先于對(duì)現(xiàn)有synchronized 塊所進(jìn)行的更新,當(dāng)進(jìn)入由同一監(jiān)控器(lock)保護(hù)的另一個(gè)synchronized 塊時(shí),將立刻可以看到這些對(duì)變量所做的更新。類(lèi)似的規(guī)則也存在于volatile變量上。
——volatile只保證可見(jiàn)性,不保證原子性!

1.3 何時(shí)要同步?

可見(jiàn)性同步的基本規(guī)則是在以下情況中必須同步:
讀取上一次可能是由另一個(gè)線(xiàn)程寫(xiě)入的變量
寫(xiě)入下一次可能由另一個(gè)線(xiàn)程讀取的變量
一致性同步:當(dāng)修改多個(gè)相關(guān)值時(shí),您想要其它線(xiàn)程原子地看到這組更改—— 要么看到全部更改,要么什么也看不到。
這適用于相關(guān)數(shù)據(jù)項(xiàng)(如粒子的位置和速率)和元數(shù)據(jù)項(xiàng)(如鏈表中包含的數(shù)據(jù)值和列表自身中的數(shù)據(jù)項(xiàng)的鏈)。

在某些情況中,您不必用同步來(lái)將數(shù)據(jù)從一個(gè)線(xiàn)程傳遞到另一個(gè),因?yàn)?JVM 已經(jīng)隱含地為您執(zhí)行同步。這些情況包括:
由靜態(tài)初始化器(在靜態(tài)字段上或 static{} 塊中的初始化器)
初始化數(shù)據(jù)時(shí)
訪(fǎng)問(wèn) final 字段時(shí) ——final對(duì)象呢?
在創(chuàng)建線(xiàn)程之前創(chuàng)建對(duì)象時(shí)
線(xiàn)程可以看見(jiàn)它將要處理的對(duì)象時(shí)

1.4 synchronize的限制

synchronized是不錯(cuò),但它并不完美。它有一些功能性的限制:
它無(wú)法中斷一個(gè)正在等候獲得鎖的線(xiàn)程;
也無(wú)法通過(guò)投票得到鎖,如果不想等下去,也就沒(méi)法得到鎖;
同步還要求鎖的釋放只能在與獲得鎖所在的堆棧幀相同的堆棧幀中進(jìn)行,多數(shù)情況下,這沒(méi)問(wèn)題(而且與異常處理交互得很好),但是,確實(shí)存在一些非塊結(jié)構(gòu)的鎖定更合適的情況。

2、ReentrantLock

java.util.concurrent.lock 中的Lock 框架是鎖定的一個(gè)抽象,它允許把鎖定的實(shí)現(xiàn)作為 Java 類(lèi),而不是作為語(yǔ)言的特性來(lái)實(shí)現(xiàn)。這就為L(zhǎng)ock 的多種實(shí)現(xiàn)留下了空間,各種實(shí)現(xiàn)可能有不同的調(diào)度算法、性能特性或者鎖定語(yǔ)義。
ReentrantLock 類(lèi)實(shí)現(xiàn)了Lock ,它擁有與synchronized 相同的并發(fā)性和內(nèi)存語(yǔ)義,但是添加了類(lèi)似鎖投票、定時(shí)鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭(zhēng)用情況下更佳的性能。(換句話(huà)說(shuō),當(dāng)許多線(xiàn)程都想訪(fǎng)問(wèn)共享資源時(shí),JVM 可以花更少的時(shí)候來(lái)調(diào)度線(xiàn)程,把更多時(shí)間用在執(zhí)行線(xiàn)程上。)

class Outputter1 {    private Lock lock = new ReentrantLock();// 鎖對(duì)象    public void output(String name) {           lock.lock();      // 得到鎖    try {    for(int i = 0; i < name.length(); i++) {    System.out.print(name.charAt(i));    }    } finally {    lock.unlock();// 釋放鎖    }    }    
}    

區(qū)別:
需要注意的是,用sychronized修飾的方法或者語(yǔ)句塊在代碼執(zhí)行完之后鎖自動(dòng)釋放,而是用Lock需要我們手動(dòng)釋放鎖,所以為了保證鎖最終被釋放(發(fā)生異常情況),要把互斥區(qū)放在try內(nèi),釋放鎖放在finally內(nèi)!!
3、讀寫(xiě)鎖ReadWriteLock

上例中展示的是和synchronized相同的功能,那Lock的優(yōu)勢(shì)在哪里?
例如一個(gè)類(lèi)對(duì)其內(nèi)部共享數(shù)據(jù)data提供了get()和set()方法,如果用synchronized,則代碼如下:

class syncData {        private int data;// 共享數(shù)據(jù)        public synchronized void set(int data) {    System.out.println(Thread.currentThread().getName() + "準(zhǔn)備寫(xiě)入數(shù)據(jù)");    try {    Thread.sleep(20);    } catch (InterruptedException e) {    e.printStackTrace();    }    this.data = data;    System.out.println(Thread.currentThread().getName() + "寫(xiě)入" + this.data);    }       public synchronized  void get() {    System.out.println(Thread.currentThread().getName() + "準(zhǔn)備讀取數(shù)據(jù)");    try {    Thread.sleep(20);    } catch (InterruptedException e) {    e.printStackTrace();    }    System.out.println(Thread.currentThread().getName() + "讀取" + this.data);    }    
}    

然后寫(xiě)個(gè)測(cè)試類(lèi)來(lái)用多個(gè)線(xiàn)程分別讀寫(xiě)這個(gè)共享數(shù)據(jù):

public static void main(String[] args) {    
//        final Data data = new Data();    final syncData data = new syncData();    
//        final RwLockData data = new RwLockData();    //寫(xiě)入  for (int i = 0; i < 3; i++) {    Thread t = new Thread(new Runnable() {    @Override  public void run() {    for (int j = 0; j < 5; j++) {    data.set(new Random().nextInt(30));    }    }    });  t.setName("Thread-W" + i);  t.start();  }    //讀取  for (int i = 0; i < 3; i++) {    Thread t = new Thread(new Runnable() {    @Override  public void run() {    for (int j = 0; j < 5; j++) {    data.get();    }    }    });    t.setName("Thread-R" + i);  t.start();  }    }    

運(yùn)行結(jié)果:
[plain] view plain copy
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入0
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入1
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R1讀取1
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R1讀取1
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R1讀取1
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R1讀取1
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R1讀取1
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R2讀取1
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R2讀取1
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R2讀取1
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R2讀取1
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R2讀取1
Thread-R0準(zhǔn)備讀取數(shù)據(jù) //R0和R2可以同時(shí)讀取,不應(yīng)該互斥!
Thread-R0讀取1
Thread-R0準(zhǔn)備讀取數(shù)據(jù)
Thread-R0讀取1
Thread-R0準(zhǔn)備讀取數(shù)據(jù)
Thread-R0讀取1
Thread-R0準(zhǔn)備讀取數(shù)據(jù)
Thread-R0讀取1
Thread-R0準(zhǔn)備讀取數(shù)據(jù)
Thread-R0讀取1
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入18
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入16
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入19
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入21
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入4
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入10
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入4
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入1
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入14
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入2
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入4
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入20
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入29

現(xiàn)在一切都看起來(lái)很好!各個(gè)線(xiàn)程互不干擾!等等。。讀取線(xiàn)程和寫(xiě)入線(xiàn)程互不干擾是正常的,但是兩個(gè)讀取線(xiàn)程是否需要互不干擾??
對(duì)!讀取線(xiàn)程不應(yīng)該互斥!
我們可以用讀寫(xiě)鎖ReadWriteLock實(shí)現(xiàn):
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class Data {        private int data;// 共享數(shù)據(jù)    private ReadWriteLock rwl = new ReentrantReadWriteLock();       public void set(int data) {    rwl.writeLock().lock();// 取到寫(xiě)鎖    try {    System.out.println(Thread.currentThread().getName() + "準(zhǔn)備寫(xiě)入數(shù)據(jù)");    try {    Thread.sleep(20);    } catch (InterruptedException e) {    e.printStackTrace();    }    this.data = data;    System.out.println(Thread.currentThread().getName() + "寫(xiě)入" + this.data);    } finally {    rwl.writeLock().unlock();// 釋放寫(xiě)鎖    }    }       public void get() {    rwl.readLock().lock();// 取到讀鎖    try {    System.out.println(Thread.currentThread().getName() + "準(zhǔn)備讀取數(shù)據(jù)");    try {    Thread.sleep(20);    } catch (InterruptedException e) {    e.printStackTrace();    }    System.out.println(Thread.currentThread().getName() + "讀取" + this.data);    } finally {    rwl.readLock().unlock();// 釋放讀鎖    }    }    
}    

測(cè)試結(jié)果:
[plain] view plain copy
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入9
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入24
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入12
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入22
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入15
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入6
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入13
Thread-W0準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W0寫(xiě)入0
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入23
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入24
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入24
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入17
Thread-W2準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W2寫(xiě)入11
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R0準(zhǔn)備讀取數(shù)據(jù)
Thread-R0讀取11
Thread-R1讀取11
Thread-R2讀取11
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入18
Thread-W1準(zhǔn)備寫(xiě)入數(shù)據(jù)
Thread-W1寫(xiě)入1
Thread-R0準(zhǔn)備讀取數(shù)據(jù)
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R2讀取1
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R1讀取1
Thread-R0讀取1
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R0準(zhǔn)備讀取數(shù)據(jù)
Thread-R0讀取1
Thread-R2讀取1
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R1讀取1
Thread-R0準(zhǔn)備讀取數(shù)據(jù)
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R0讀取1
Thread-R2讀取1
Thread-R1讀取1
Thread-R0準(zhǔn)備讀取數(shù)據(jù)
Thread-R1準(zhǔn)備讀取數(shù)據(jù)
Thread-R2準(zhǔn)備讀取數(shù)據(jù)
Thread-R1讀取1
Thread-R2讀取1
Thread-R0讀取1

與互斥鎖定相比,讀-寫(xiě)鎖定允許對(duì)共享數(shù)據(jù)進(jìn)行更高級(jí)別的并發(fā)訪(fǎng)問(wèn)。雖然一次只有一個(gè)線(xiàn)程(writer 線(xiàn)程)可以修改共享數(shù)據(jù),但在許多情況下,任何數(shù)量的線(xiàn)程可以同時(shí)讀取共享數(shù)據(jù)(reader 線(xiàn)程)

從理論上講,與互斥鎖定相比,使用讀-寫(xiě)鎖定所允許的并發(fā)性增強(qiáng)將帶來(lái)更大的性能提高。
在實(shí)踐中,只有在多處理器上并且只在訪(fǎng)問(wèn)模式適用于共享數(shù)據(jù)時(shí),才能完全實(shí)現(xiàn)并發(fā)性增強(qiáng)?!?#xff0c;某個(gè)最初用數(shù)據(jù)填充并且之后不經(jīng)常對(duì)其進(jìn)行修改的 collection,因?yàn)榻?jīng)常對(duì)其進(jìn)行搜索(比如搜索某種目錄),所以這樣的 collection 是使用讀-寫(xiě)鎖定的理想候選者。

4、線(xiàn)程間通信Condition

Condition可以替代傳統(tǒng)的線(xiàn)程間通信,用await()替換wait(),用signal()替換notify(),用signalAll()替換notifyAll()。
——為什么方法名不直接叫wait()/notify()/nofityAll()?因?yàn)镺bject的這幾個(gè)方法是final的,不可重寫(xiě)!

傳統(tǒng)線(xiàn)程的通信方式,Condition都可以實(shí)現(xiàn)。
注意,Condition是被綁定到Lock上的,要?jiǎng)?chuàng)建一個(gè)Lock的Condition必須用newCondition()方法。

Condition的強(qiáng)大之處在于它可以為多個(gè)線(xiàn)程間建立不同的Condition
看JDK文檔中的一個(gè)例子:假定有一個(gè)綁定的緩沖區(qū),它支持 put 和 take 方法。如果試圖在空的緩沖區(qū)上執(zhí)行 take 操作,則在某一個(gè)項(xiàng)變得可用之前,線(xiàn)程將一直阻塞;如果試圖在滿(mǎn)的緩沖區(qū)上執(zhí)行 put 操作,則在有空間變得可用之前,線(xiàn)程將一直阻塞。我們喜歡在單獨(dú)的等待 set 中保存put 線(xiàn)程和take 線(xiàn)程,這樣就可以在緩沖區(qū)中的項(xiàng)或空間變得可用時(shí)利用最佳規(guī)劃,一次只通知一個(gè)線(xiàn)程。可以使用兩個(gè)Condition 實(shí)例來(lái)做到這一點(diǎn)。
——其實(shí)就是java.util.concurrent.ArrayBlockingQueue的功能

class BoundedBuffer {  final Lock lock = new ReentrantLock();          //鎖對(duì)象  final Condition notFull  = lock.newCondition(); //寫(xiě)線(xiàn)程鎖  final Condition notEmpty = lock.newCondition(); //讀線(xiàn)程鎖  final Object[] items = new Object[100];//緩存隊(duì)列  int putptr;  //寫(xiě)索引  int takeptr; //讀索引  int count;   //隊(duì)列中數(shù)據(jù)數(shù)目  //寫(xiě)  public void put(Object x) throws InterruptedException {  lock.lock(); //鎖定  try {  // 如果隊(duì)列滿(mǎn),則阻塞<寫(xiě)線(xiàn)程>  while (count == items.length) {  notFull.await();   }  // 寫(xiě)入隊(duì)列,并更新寫(xiě)索引  items[putptr] = x;   if (++putptr == items.length) putptr = 0;   ++count;  // 喚醒<讀線(xiàn)程>  notEmpty.signal();   } finally {   lock.unlock();//解除鎖定   }   }  //讀   public Object take() throws InterruptedException {   lock.lock(); //鎖定   try {  // 如果隊(duì)列空,則阻塞<讀線(xiàn)程>  while (count == 0) {  notEmpty.await();  }  //讀取隊(duì)列,并更新讀索引  Object x = items[takeptr];   if (++takeptr == items.length) takeptr = 0;  --count;  // 喚醒<寫(xiě)線(xiàn)程>  notFull.signal();   return x;   } finally {   lock.unlock();//解除鎖定   }   }   

優(yōu)點(diǎn):

假設(shè)緩存隊(duì)列中已經(jīng)存滿(mǎn),那么阻塞的肯定是寫(xiě)線(xiàn)程,喚醒的肯定是讀線(xiàn)程,相反,阻塞的肯定是讀線(xiàn)程,喚醒的肯定是寫(xiě)線(xiàn)程。

那么假設(shè)只有一個(gè)Condition會(huì)有什么效果呢?緩存隊(duì)列中已經(jīng)存滿(mǎn),這個(gè)Lock不知道喚醒的是讀線(xiàn)程還是寫(xiě)線(xiàn)程了,如果喚醒的是讀線(xiàn)程,皆大歡喜,如果喚醒的是寫(xiě)線(xiàn)程,那么線(xiàn)程剛被喚醒,又被阻塞了,這時(shí)又去喚醒,這樣就浪費(fèi)了很多時(shí)間。
轉(zhuǎn)自:http://blog.csdn.net/vking_wang/article/details/9952063

總結(jié)

以上是生活随笔為你收集整理的【java线程】锁机制:synchronized、Lock、Condition的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。