多線程的同步
多線程同步控制機制的目的是保證同一時刻只有一個線程訪問共享數據。java中,要實現多線程的同步,可使用同步方法和同步代碼塊兩種機制。
1. 同步方法
使用synchronized修飾的方法稱爲同步方法,它意味着同一時刻該方法只能被一個線程執行,其它想執行該方法的線程必須等待(處於不可運行狀態),直到獲得同步鎖之後才能執行(處於可運行狀態)。
public synchronized void test() {
// 代碼段
}
注(同步鎖在哪兒):
(1)如果synchronized修飾的是實例方法,那麼鎖對象就是this。即,多個併發線程實際上是在實例方法所屬的對象上加鎖和獲取鎖。加鎖和釋放鎖的過程由java虛擬機自動完成。
(2)如果synchronized修飾的是類中的靜態方法,那麼鎖對象就是該類的Class實例。
(3)如果類中有多個synchronized方法:只要一個線程訪問了其中一個synchronized方法,那麼其它線程便不能同
時執行任何其它synchronized方法。當然,不同的對象實例中的synchronized方法是互不影響。也就是說,其
它線程可以同時訪問另一個對象實例中的synchronized方法 。
(4)同步方法應儘可能小,即,同步方法中儘量只包含涉及共享資源的訪問代碼。其它不涉及共享資源的代碼不必放入同步方法中,以提高線程的併發性。如果將run()方法修飾爲synchronized,則線程併發性最差。
2. 同步代碼塊
創建一個用作同步鎖的對象:
Object lock = new Object();
public void test1() {
synchronized (lock) { //在對象上加鎖、或從對象上獲取鎖
// 代碼段
}
}
注:
(1)務必保證使用同一個對象作爲同步鎖對象。
(2)如果一個線程得到了鎖並進入了休眠狀態(sleep),它仍然不會釋放鎖,直至同步塊中的所有代碼執行完畢。
多線程的同步與協作——生產者消費者問題
多個生產者(線程)給一個容器中放入產品(數據),多個消費者(線程)從容器中獲取產品進行消費。容器放滿後,生產者需要等待;容器爲空時,消費者需要等待。生產者和消費者不能同時訪問容器(互斥操作)。
此問題不僅有共享變量的互斥訪問,而且還存在協作工作的問題。
import java.util.LinkedList;
import java.util.List;
public class Test2 {
public static final int MAX_SIZE = 10;
public static void main(String[] args) {
// Auto-generated method stub
List<String> container = new LinkedList<String>();
Producer p1 = new Producer(container, "p1");
Producer p2 = new Producer(container, "p2");
Producer p3 = new Producer(container, "p3");
Producer p4 = new Producer(container, "p4");
Consumer c1 = new Consumer(container, "c1");
Consumer c2 = new Consumer(container, "c2");
Consumer c3 = new Consumer(container, "c3");
p1.start();
p2.start();
p3.start();
p4.start();
c1.start();
c2.start();
c3.start();
}
}
class Producer extends Thread {
private List<String> container = null;
public Producer(List<String> con, String name) {
super(name);
this.container = con;
}
@Override
public void run() {
// 生產者
int i = 0;
String product = null;
while (i < 20) {
i++;
product = Thread.currentThread().getName() + ":" + i;
synchronized (container) {
while (container.size() >= Test2.MAX_SIZE) {
try {
container.wait();
} catch (InterruptedException e) {
}
}
container.add(product);
container.notifyAll();
}
// 釋放鎖
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}
class Consumer extends Thread {
private List<String> container = null;
public Consumer(List<String> con, String name) {
super(name);
this.container = con;
}
@Override
public void run() {
// 消費者
String prd = null;
while (true) {
synchronized (container) {
while (container.size() == 0) {
try {
container.wait();
} catch (InterruptedException e) {
}
}
prd = container.remove(0);
container.notifyAll();
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+"消費了產品:"+prd);
}
}
}