Thread API

      在〈Thread API〉中有 1 則留言

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;
} <==有傳回值

🔒 底下內容僅限會員閱讀。

立即登入

1 thought on “Thread API

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *