在多線程程序執行過程中,可能會涉及到兩個或者多個線程試圖同一時候訪問同一個資源。為了防止這樣的情況的發生,必須在線程使用共享資源時給資源“上鎖”,以阻擋其他線程的訪問。
而這樣的機制也經常被稱為相互排斥量。本文主要介紹它的兩種方式synchronized和Lock 。
1、synchronized
當任務要運行被synchronizedkeyword保護的代碼片段的時候,它會檢查鎖是否可用,然后獲取鎖。運行代碼。釋放鎖。synchronized也有兩種使用方法:
A、synchronized方法
import java.util.concurrent.*;
import java.util.*;
public class ThreadTest implements Runnable {
public synchronized void run() { //聲明方式
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) {
ThreadTest t = new ThreadTest();
Thread t1 = new Thread(t, "A");
Thread t2 = new Thread(t, "B");
t1.start();
t2.start();
}
}/*
* Output: A0 A1 A2 A3 A4 B0 B1 B2 B3 B4
*/// :~
要注意的是,全部對象都自己主動含有單一的鎖(也稱為監視器)。所以當ThreadTest對象調用synchronized方法時,此對象被加鎖,這意味着。其它線程不能調用此對象的全部synchronized方法。 可是假設把一個復雜的方法聲明為synchronized,會減少程序執行的效率,所以synchronized塊是非常好的解決方法。
B、synchronized塊
import java.util.concurrent.*;
import java.util.*;
public class ThreadTest {
public void g() {
synchronized (this) { //聲明方式
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
public static void main(String[] args) {
final ThreadTest t = new ThreadTest();
Thread t1 = new Thread(new Runnable() {
public void run() {
t.g();
}
}, "A");
Thread t2 = new Thread(new Runnable() {
public void run() {
t.g();
}
}, "B");
t1.start();
t2.start();
}
}
/*
* Output: A0 A1 A2 A3 A4 B0 B1 B2 B3 B4
*/// :~
當一個線程在訪問object對象的一個synchronized(this)同步代碼塊時,其他線程仍然能夠訪問此對象的非同步代碼塊。
當然假設不是同一個對象
,synchronized是無法實現相互排斥的。以下是第一句話的代碼:
import java.util.concurrent.*;
import java.util.*;
public class ThreadTest {
public void g() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
public void h() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) {
final ThreadTest t = new ThreadTest();
Thread t1 = new Thread(new Runnable() {
public void run() {
t.g();
}
}, "A");
Thread t2 = new Thread(new Runnable() {
public void run() {
t.h();
}
}, "B");
t1.start();
t2.start();
}
}
/*
* Output:A0 B0 A1 B1 A2 B2 A3 B3 A4 B4
*/// :~
2、Lock
Lock和synchronized的差別就是:
synchronized:當A和B同一時候要訪問C資源,而A獲得了對象C的鎖。B將一直等待A釋放對C的鎖,不能被中斷。
Lock:B等待一定時間后,A還不釋放C,B就會中斷等待。
它的基本使用方法:
Lock l = new ReentrantLock();
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
從代碼也能夠看出,l.unlock()在finally{}中。這表示終於會被解鎖。
3、volatile
用volatile修飾的變量。線程在每次使用變量的時候,都會讀取變量改動后的最的值,它能避免因編譯器優化導致的讀值不准確,
然而它非常easy被誤用。
比方僅僅使用volatile變量來實現的累加器,無法得到正確結果。由於JAVA中的自增操作++,
並非原子性操作,在自增過程中還是出現了多線程間的交互。
