Thread API
java.util.concurrent package
在執行緒的章節中, 用synchronized來鎖定資源以避免其他執行緒相互競爭. 但有著效能問題及死結的風險. 所以Java SE 7 提供了concurrent這套件, 讓開發者可以輕易的寫出穩固的多執行緒程式.
java.util.concurrent.atomic package
atomic是原子, 也就是不可分割的意思. atomic package包含lock-free thread-safe的方法
AtomicInteger 為atomic實作的一個物件, 可確保裏面的動作在完成前, 不會被中斷. 常見的動作有
compareAndSet(5, 42);//若aotmicInteger的數字是5, 則會變成42
getAndAdd(10);//先取出, 再加10
addAndGet(10)://先加10, 再取出
getAndIncrement() : 取出再加1
getAndDecrement():取出再減1
public class AtomicTest extends Thread{
AtomicInteger atomicInteger=new AtomicInteger(10);
public void run(){
int a=atomicInteger.getAndAdd(10);
int b=atomicInteger.addAndGet(10);
int c=atomicInteger.decrementAndGet();
atomicInteger.compareAndSet(20, 50);
System.out.printf("a=%d, b=%d, c=%d, atomicInteger=%d\n", a, b, c, atomicInteger.get());
}
public static void main(String[] args) {
new AtomicTest().start();
}
}
volatile [ˋvɑlət!]
在變數前加上volatile修飾子, 如
volatile int a=10;
此即告訴JVM, 此變數是不穩定的, 所以當線程訪問時, 就會強迫從共用記憶体區重新取得該變數的值, 讓所有的線程看到的值都是同一個, 所以使用volatile會很耗費資源
只保証執行緒寫回記憶体這段是同步, 但不保証只有一個執行緒在存取這個值, 所以仍有其風險
java.util.concurrent.locks package
此為一framework, 與內建的synchronize作一個區隔. 可在執行緒間進行更為細部的互動, 更為彈性的設計.
Lock介面之下, 有ReentrantLock類別, ReentrantLock再衍生ReentrandReadWriteLock. re-entrant, 是重入的意思
如下是RetrantLock的例子
特別注意 : ReentrantLock lock必需是static, 不然的話會變成物件變數, 每個物件都有自己的鎖, 就鎖不住了
另解鎖時, 需在finally區塊, 不然發生了例外, 就解不開了
ReentrantReadWriteLock 是高效的資源鎖定, 多了lock.readLock()及lock.writeLock()二個方法, 解決執行緒在大量操作與修改時的問題.
取得readLock.lock()時機 : 沒其他執行緒取得writeLock.
取得writeLock.lock()時機 : 沒其他執行緒取得readLock及writeLock
class MyRunnable implements Runnable{
private final ReentrantReadWriteLock rw1=new ReentrantReadWriteLock();
private int i=0;
@Override
public void run() {
while (i<=100){
rw1.writeLock().lock();
i++;
rw1.writeLock().unlock();
System.out.println(Thread.currentThread().getId()+" : i : "+i);
}
}
}
Thread-Safe Collections
java.util 的collections並不是thread-safe. 習慣使用下面方式
synchronized block
封裝 synchronized , 如java.util.Collections.synchronizedList(List<T>)
使用java.util.concurrent集合
注意 : 此Collection是thread-safe, 但裏面的元素並不是thread-safe
java.util.concurrent.CyclicBarrier 回環柵欄
Cyclic [ˋsɪklɪk] :環式的 Barrier[ˋbærɪr] : 柵欄
利用await()的方式, 讓執行緒進入等待狀態, 又稱為柵欄狀態. 當進入柵欄狀態的執行緒數達到指定的數量時, 再同時開放所有的執行緒執行後續的動作
上述new CyclicBarrire(2) 表示要達到2 個執行緒進入柵欄狀態後, 才會開啟柵欄一同釋放
java.util.concurrent.Executor
執行者. Executor 是一個介面, 需實作execute(Runnable)方法, 然後要作的事全交給這個執行者去作. 其實在execute()裏要作的事就是 new thread(Runnable).start()
public class ExecutorTest {
public static void main(String[] args) {
MyExecutor e=new MyExecutor();
e.execute(new TimeRunnable());
e.execute(new TimeRunnable());
e.execute(new TimeRunnable());
}
}
class MyExecutor implements Executor{
@Override
public void execute(Runnable r) {
new Thread(r).start();
}
}
class TimeRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" : 目前時間"+ new Date());
}
}
java.util.concurrent.Callable
要建立一個新的執行緒時, 通常都會先實作Runnable, 然後再把Runnable放入Thread內
現在, 除了Runnable, 還可以實作Callable, 然後放入FutureTask內, FutureTask再放入Thread內執行, 此時就可以用FutureTask取得傳回值
二者差異 :
public interface Runnable{
public abstract void run();
} <==無傳回值
public interface Callable<V>{
V call() throws Exception;
} <==有傳回值

This blog inspires me to continue my own passions, appreciate it.