一盅称、synchronized 關(guān)鍵字
Synchronized 是Java中的關(guān)鍵字、是一種同步鎖。其作用主要有:
- 確保線程互斥的訪問(wèn)同步代碼
- 保證共享變量的修改能夠及時(shí)可見(jiàn)
- 有效解決指令重排序問(wèn)題
從語(yǔ)法上講缩膝,Synchronized有三種用法:
- 修飾普通方法混狠,被修飾的方法稱(chēng)為同步方法,其作用的范圍是整個(gè)方法逞盆,作用的對(duì)象是調(diào)用這個(gè)方法的對(duì)象檀蹋;
- 修飾靜態(tài)方法,其作用的范圍是整個(gè)靜態(tài)方法云芦,作用的對(duì)象是這個(gè)類(lèi)的所有對(duì)象俯逾;
- 修飾代碼塊,被修飾的代碼塊稱(chēng)為同步語(yǔ)句塊舅逸,其作用的范圍是大括號(hào){}括起來(lái)的代碼桌肴,作用的對(duì)象是調(diào)用這個(gè)代碼塊的對(duì)象;
- 修飾一個(gè)類(lèi)琉历,其作用的范圍是synchronized后面括號(hào)括起來(lái)的部分坠七,作用主的對(duì)象是這個(gè)類(lèi)的所有對(duì)象。
釋放鎖只會(huì)有兩種情況:
- 獲取鎖的線程執(zhí)行完了該代碼塊旗笔,然后線程釋放對(duì)鎖的占有彪置;
- 線程執(zhí)行發(fā)生異常,此時(shí)JVM會(huì)讓線程自動(dòng)釋放鎖蝇恶。
更多請(qǐng)參見(jiàn):[java中synchronized關(guān)鍵字的用法】(http://www.cnblogs.com/wl0000-03/p/5973039.html)
二拳魁、Lock
Lock和synchronied需要注意以下幾點(diǎn):
- synchronized是Java語(yǔ)言的關(guān)鍵字,Lock是一個(gè)類(lèi)撮弧。
- 采用synchronized不需要用戶去手動(dòng)釋放鎖潘懊,當(dāng)synchronized方法或者synchronized代碼塊執(zhí)行完之后,系統(tǒng)會(huì)自動(dòng)讓線程釋放對(duì)鎖的占用贿衍;而Lock則必須要用戶去手動(dòng)釋放鎖授舟,如果沒(méi)有主動(dòng)釋放鎖,就有可能導(dǎo)致出現(xiàn)死鎖現(xiàn)象贸辈。
- 在資源競(jìng)爭(zhēng)不是很激烈的情況下释树,Synchronized的性能要優(yōu)于ReetrantLock,但是在資源競(jìng)爭(zhēng)很激烈的情況下裙椭,Synchronized的性能會(huì)下降幾十倍躏哩,
- Lock可以讓等待鎖的線程響應(yīng)中斷,而synchronized卻不行揉燃,使用synchronized時(shí)扫尺,等待的線程會(huì)一直等待下去,不能夠響應(yīng)中斷炊汤;
- 通過(guò)Lock可以知道有沒(méi)有成功獲取鎖正驻,而synchronized卻無(wú)法辦到弊攘。
- Lock可以提高多個(gè)線程進(jìn)行讀操作的效率。
但是ReetrantLock的性能能維持常態(tài)姑曙;
Lock的源碼:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
首先lock()方法是平常使用得最多的一個(gè)方法襟交,就是用來(lái)獲取鎖。如果鎖已被其他線程獲取伤靠,則進(jìn)行等待捣域。由于在前面講到如果采用Lock,必須主動(dòng)去釋放鎖宴合,并且在發(fā)生異常時(shí)焕梅,不會(huì)自動(dòng)釋放鎖。因此一般來(lái)說(shuō)卦洽,使用Lock必須在try{}catch{}塊中進(jìn)行贞言,并且將釋放鎖的操作放在finally塊中進(jìn)行,以保證鎖一定被被釋放阀蒂,防止死鎖的發(fā)生该窗。通常使用Lock來(lái)進(jìn)行同步的話,是以下面這種形式去使用的:
Lock lock = ...;
lock.lock();
try{
//處理任務(wù)
}catch(Exception ex){
}finally{
lock.unlock(); //釋放鎖
}
tryLock()方法是有返回值的蚤霞,它表示用來(lái)嘗試獲取鎖酗失,如果獲取成功,則返回true昧绣,如果獲取失斆ど蕖(即鎖已被其他線程獲忍摺)舍肠,則返回false沛婴,也就說(shuō)這個(gè)方法無(wú)論如何都會(huì)立即返回抖苦。在拿不到鎖時(shí)不會(huì)一直在那等待堵漱。
Lock lock = ...;
lock.lock();
try{
//處理任務(wù)
}catch(Exception ex){
}finally{
lock.unlock(); //釋放鎖
}
lockInterruptibly()方法比較特殊夜牡,當(dāng)通過(guò)這個(gè)方法去獲取鎖時(shí)芳誓,如果線程正在等待獲取鎖醉锅,則這個(gè)線程能夠響應(yīng)中斷兔簇,即中斷線程的等待狀態(tài)。也就使說(shuō)硬耍,當(dāng)兩個(gè)線程同時(shí)通過(guò)lock.lockInterruptibly()想獲取某個(gè)鎖時(shí)垄琐,假若此時(shí)線程A獲取到了鎖,而線程B只有在等待经柴,那么對(duì)線程B調(diào)用threadB.interrupt()方法能夠中斷線程B的等待過(guò)程狸窘。
由于lockInterruptibly()的聲明中拋出了異常,所以lock.lockInterruptibly()必須放在try塊中或者在調(diào)用lockInterruptibly()的方法外聲明拋出InterruptedException坯认。
因此lockInterruptibly()一般的使用形式如下:
public void method() throws InterruptedException {
lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}
Lock 的簡(jiǎn)單示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket implements Runnable {
// 定義票
private int tickets = 100;
// 定義鎖對(duì)象
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
// 加鎖
lock.lock();
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "張票");
}
} finally {
// 釋放鎖
lock.unlock();
}
}
}
}
三翻擒、ReentrantLock
如果鎖具備可重入性氓涣,則稱(chēng)作為可重入鎖。像synchronized和ReentrantLock都是可重入鎖陋气,可重入性在我看來(lái)實(shí)際上表明了鎖的分配機(jī)制:基于線程的分配劳吠,而不是基于方法調(diào)用的分配。舉個(gè)簡(jiǎn)單的例子巩趁,當(dāng)一個(gè)線程執(zhí)行到某個(gè)synchronized方法時(shí)痒玩,比如說(shuō)method1,而在method1中會(huì)調(diào)用另外一個(gè)synchronized方法method2议慰,此時(shí)線程不必重新去申請(qǐng)鎖蠢古,而是可以直接執(zhí)行方法method2。
class MyClass {
public synchronized void method1() {
method2();
}
public synchronized void method2() {
}
}
簡(jiǎn)單示例
- lock()的正確使用方法
public class Test {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
private Lock lock = new ReentrantLock(); //注意這個(gè)地方
public static void main(String[] args) {
final Test test = new Test();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
}
public void insert(Thread thread) {
lock.lock();
try {
System.out.println(thread.getName()+"得到了鎖");
for(int i=0;i<5;i++) {
arrayList.add(i);
}
} catch (Exception e) {
}finally {
System.out.println(thread.getName()+"釋放了鎖");
lock.unlock();
}
}
}
- tryLock()的使用方法
public class Test {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
private Lock lock = new ReentrantLock(); //注意這個(gè)地方
public static void main(String[] args) {
final Test test = new Test();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
}
public void insert(Thread thread) {
if(lock.tryLock()) {
try {
System.out.println(thread.getName()+"得到了鎖");
for(int i=0;i<5;i++) {
arrayList.add(i);
}
} catch (Exception e) {
// TODO: handle exception
}finally {
System.out.println(thread.getName()+"釋放了鎖");
lock.unlock();
}
} else {
System.out.println(thread.getName()+"獲取鎖失敗");
}
}
}
- lockInterruptibly()響應(yīng)中斷的使用方法
public class Test {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
Test test = new Test();
MyThread thread1 = new MyThread(test);
MyThread thread2 = new MyThread(test);
thread1.start();
thread2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.interrupt();
}
public void insert(Thread thread) throws InterruptedException{
lock.lockInterruptibly(); //注意褒脯,如果需要正確中斷等待鎖的線程便瑟,必須將獲取鎖放在外面,然后將InterruptedException拋出
try {
System.out.println(thread.getName()+"得到了鎖");
long startTime = System.currentTimeMillis();
for( ; ;) {
if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
break;
//插入數(shù)據(jù)
}
}
finally {
System.out.println(Thread.currentThread().getName()+"執(zhí)行finally");
lock.unlock();
System.out.println(thread.getName()+"釋放了鎖");
}
}
}
class MyThread extends Thread {
private Test test = null;
public MyThread(Test test) {
this.test = test;
}
@Override
public void run() {
try {
test.insert(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"被中斷");
}
}
}
四番川、ReadWriteLock
ReadWriteLock也是一個(gè)接口到涂,在它里面只定義了兩個(gè)方法:
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
五、ReentrantReadWriteLock
ReentrantReadWriteLock里面提供了很多豐富的方法颁督,不過(guò)最主要的有兩個(gè)方法:readLock()和writeLock()用來(lái)獲取讀鎖和寫(xiě)鎖践啄。
示例
public class Test {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
final Test test = new Test();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
}
public void get(Thread thread) {
rwl.readLock().lock();
try {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName()+"正在進(jìn)行讀操作");
}
System.out.println(thread.getName()+"讀操作完畢");
} finally {
rwl.readLock().unlock();
}
}
}
更多內(nèi)容
1. Java 并發(fā)編程:核心理論
2. 聊聊并發(fā)(一)深入分析Volatile的實(shí)現(xiàn)原理
3. Java中Synchronized的用法
4. Java并發(fā)編程:Lock
5. 死磕Java系列博客