執行緒同步基礎
阿新 • • 發佈:2019-01-30
class Cinema { private long vacanciesCinema1; private long vacanciesCinema2; private final Object controlCinema1; private final Object controlCinema2; public Cinema() { controlCinema1 = new Object(); controlCinema2 = new Object(); vacanciesCinema1 = 20; vacanciesCinema2 = 20; } public boolean sellTickets1(long number) { synchronized (controlCinema1) { if (number <= vacanciesCinema1) { vacanciesCinema1 -= number; return true; } else { return false; } } } public boolean sellTickets2(long number) { synchronized (controlCinema2) { if (number <= vacanciesCinema2) { vacanciesCinema2 -= number; return true; } else { return false; } } } public long getVacanciesCinema1() { return vacanciesCinema1; } public long getVacanciesCinema2() { return vacanciesCinema2; } }
3. wait/notify/notifyAll
在生產者-消費者模型中,通常用一個buffer作為共享的資料結構,當buffer滿的時候,生產者不能再放入資料;當buffer空的時候,消費者不能取資料。對於以上兩種情況,java在Object類中提供了wait notify notifyAll方法。wait方法需要在同步程式碼塊中被呼叫,否則,將會丟擲IllegalMonitorStateException。 當被呼叫時,JVM將會使當前執行緒sleep,同時釋放控制同步程式碼塊的物件鎖,必須使用nofity或者notifyAll來喚醒該執行緒。
class EventStorage { int maxSize; LinkedList<Date> storage; public EventStorage() { maxSize = 10; storage = new LinkedList<>(); } public synchronized void set() { while (storage.size() == maxSize) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } storage.offer(new Date()); System.out.printf("Set: %d\n", storage.size()); notifyAll(); } public synchronized void get() { while (storage.size() == 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } storage.poll(); System.out.printf("Get: %d\n", storage.size()); notifyAll(); } }
4. Lock
Java提供了另一種同步程式碼塊的機制,它是基於Lock介面和它的實現類(例如ReentrantLock)來實現的,這種機制更加強大和靈活,主要的優點表現在:1)Lock介面允許更加複雜的結構,synchronized關鍵字必須要用結構化的方法來獲取或者釋放同步程式碼塊;
2)Lock介面提供了一些額外的功能。例如tryLock()方法。
3)當只有一個寫和多個讀的執行緒時,Lock介面允許讀寫操作的分離
4)Lock介面的效能更高
class PrintQueue { private final Lock queueLock = new ReentrantLock(); public void printJob(Object document) { queueLock.lock(); try { long duration = (long)(Math.random()*10000); System.out.println(Thread.currentThread().getName() + ": PrintQueue: Printing a Job during " + (duration/1000) + " seconds"); Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } finally { queueLock.unlock(); } } } class Job implements Runnable { private PrintQueue printQueue; public Job(PrintQueue printQueue) { this.printQueue = printQueue; } @Override public void run() { System.out.printf("%s: Going to print a document\n", Thread.currentThread().getName()); printQueue.printJob(new Object()); System.out.printf("%s: The document has been printed\n", Thread.currentThread().getName()); } }
5. 讀寫鎖
ReadWriteLock是所有Lock介面中最重要的介面之一,ReentrentReadWriteLock是它的唯一實現類。該類有兩個鎖,一個是讀操作另一個是寫操作。它能夠同時包括多個讀操作,但是隻能有一個寫操作。當某個執行緒執行寫操作時,其他任何執行緒都不能執行讀操作。
class PricesInfo {
private double price1;
private double price2;
private ReadWriteLock lock;
public PricesInfo() {
price1 = 1.0;
price2 = 2.0;
lock = new ReentrantReadWriteLock();
}
public double getPrice1() {
lock.readLock().lock();
double value = price1;
lock.readLock().unlock();
return value;
}
public double getPrice2() {
lock.readLock().lock();
double value = price2;
lock.readLock().unlock();
return value;
}
public void setPrices(double price1, double price2) {
lock.writeLock().lock();
this.price1 = price1;
this.price2 = price2;
lock.writeLock().unlock();
}
}
6. 公平鎖
ReentrantLock和ReentrantReadWriteLock的建構函式可以傳入一個boolean型的引數fair,該引數預設為false,如果設定為true,則在多個執行緒等待同一個鎖時,選擇等待時間最長的執行緒優先執行。
class MessageQueue implements MyQueue {
private Lock queueLock = new ReentrantLock(true);
public void printJob(Object document) {
queueLock.lock();
try {
long duration = (long)(Math.random()*10000);
System.out.println(Thread.currentThread().getName() + ": PrintQueue: Printing a Job during " + (duration/1000) + " seconds");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
queueLock.unlock();
}
queueLock.lock();
try {
long duration = (long)(Math.random()*10000);
System.out.println(Thread.currentThread().getName() + ": PrintQueue: Printing a Job during " + (duration/1000) + " seconds");
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
queueLock.unlock();
}
}
}
7. 一個鎖的多個condition
class Buffer {
private LinkedList<String> buffer;
private int maxSize;
private ReentrantLock lock;
private Condition lines;
private Condition space;
private boolean pendingLines;
public Buffer(int maxSize) {
this.maxSize = maxSize;
buffer = new LinkedList<>();
lock = new ReentrantLock();
lines = lock.newCondition();
space = lock.newCondition();
pendingLines = true; //是否還會有lines insert到Buffer中
}
public void insert(String line) {
lock.lock();
try {
while(buffer.size() == maxSize) {
space.wait();
}
buffer.offer(line);
System.out.printf("%s: Inserted Line: %d\n", Thread.currentThread().getName(), buffer.size());
lines.signalAll();
}
catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public String get() {
String line = null;
lock.lock();
try {
while ((buffer.size() == 0) && hasPendingLines()) {
lines.await();
}
if(hasPendingLines()) {
line = buffer.poll();
System.out.printf("%s: Line Readed: %d\n", Thread.currentThread().getName(), buffer.size());
space.signalAll();
}
} catch(InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock();
}
return line;
}
public boolean hasPendingLines() {
return pendingLines || buffer.size() > 0;
}
public void setPendingLines(boolean pendingLines) {
this.pendingLines = pendingLines;
}
}