Java實現線程通訊的幾種方式

本文共總結了5種方式,經過代碼舉例的方式進行展現。5種方式爲:synchronized加wait/notify方式、ReentrantLock加Condition方式、閉鎖的方式、柵欄的方式、信號量的方式。java

最後還介紹了join和yield的使用。ide

一、synchronized加wait/notify方式ui

/**
 * wait和notify的使用
 * wait和notify必須應用在synchronized塊或方法內
 * 下面的代碼向跳交誼舞同樣互相控制着對方的輸出
 */
public class MutiThread_WaitNotify {
    public static void main(String[] args) {
        final Object lock = new Object();
        Thread a = new Thread(new Runnable(){
            @Override
            public void run(){
                synchronized (lock){
                    try{
                        lock.wait();
                        System.out.println("A-1");
                        lock.notify();
                        lock.wait();
                        System.out.println("A-2");
                        lock.notify();
                        lock.wait();
                        System.out.println("A-3");
                        lock.notify();
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        });
        Thread b = new Thread(new Runnable(){
            @Override
            public void run(){
                synchronized (lock){
                    try{
                        System.out.println("B-1");
                        lock.notify();
                        lock.wait();
                        System.out.println("B-2");
                        lock.notify();
                        lock.wait();
                        System.out.println("B-3");
                        lock.notify();
                        lock.wait();
                        System.out.println("B-4");
                    }catch(InterruptedException e){
                        e.printStackTrace();;
                    }
                }
            }
        });
        a.start();
        b.start();
    }
}

二、ReentrantLock加Condition方式線程

/**
 * ReentrantLock和Condition的使用
 * 在使用Conditioin的await和signal時,必須將這兩個方法寫在ReentrantLock的lock方法以後
 */
public class MutiThread_ReentrantLock_Condition {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        int i=1;
        for(; i<=6; i++){
            final int k = i;
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        lock.lock();
                        System.out.println("ThreadNo:A" + k + " is locked");
                        // 經過condition.await將線程阻塞
                        condition.await();
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }finally{
                        lock.unlock();
                        System.out.println("ThreadNo:A"+k + " is unlocked");
                    }
                }
            });

            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    if(k == 6){
                        try{
                            lock.lock();
                            System.out.println("All Threads is signaled");
                            // 經過condition.signalAll喚醒全部線程
                            condition.signalAll();
                        }catch(Exception e){
                            e.printStackTrace();
                        }finally{
                            lock.unlock();
                        }
                    }else{
                        System.out.println("threads can't signaled, wait a moment.");
                    }
                }
            });
            t1.start();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            t2.start();
        }
    }
}

三、閉鎖方式事件

import java.util.concurrent.CountDownLatch;
/**
 * 閉鎖的使用
 * 閉鎖用於等待事件,當閉鎖到達結束狀態(本例中是閉鎖的計數器值減爲0)以前,全部線程都等待,當閉鎖到達結束狀態時,全部線程都經過
 * 閉鎖是一次性的,當閉鎖到達結束狀態後,將不會被重置,這個鎖會永遠打開並容許全部線程經過。
 * 能夠將代碼中的NUM變量值變爲2和4,分別試試什麼效果
 */
public class MutiThread_CountDownLatch {
    public static void main(String[] args) {
        // 定義閉鎖,並設置閉鎖的計數器值爲3
        CountDownLatch lock = new CountDownLatch(3);
        // 循環定義3個線程
        int NUM = 3;
        for(int i=1; i<=NUM; i++){
            final int k = i;
            Thread a = new Thread(new Runnable(){
                @Override
                public void run(){
                    try{
                        Thread.sleep(k * 1000);
                        System.out.println("ThreadNo:A"+k);
                        // 每一個線程在休眠指定時間後將閉鎖的計數器值減1,當閉鎖的計數器值減到0時,閉所將被打開,從而使第二個循環中的全部線程才能經過
                        lock.countDown();
                        // 打印閉鎖計數器的值
                        System.out.println("ThreadNo:A"+k+"; getCount:"+lock.getCount());
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            });
            a.start();
        }
        // 循環定義2個線程
        for(int i=1; i<=2; i++){
            final int k = i;
            Thread b = new Thread(new Runnable(){
                @Override
                public void run(){
                    try{
                        System.out.println("ThreadNo:B"+k+" is waiting...");
                        // 當閉鎖的計數器值不爲0時,線程將在此處被中斷
                        lock.await();
                        // 當閉鎖的計數器值等於0時,閉鎖將被打開,全部等待的線程都將被喚醒
                        System.out.println("ThreadNo:B"+k+" is notify");
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            });
            b.start();
        }
    }
}

四、柵欄的方式資源

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * 柵欄的使用
 * 柵欄用於等待線程,全部線程必須同時到達柵欄,才能繼續執行
 * 柵欄不是一次性的,能夠被重置。
 * 能夠將代碼中的NUM變量值變爲5和7,分別試試什麼效果
 */
public class MutiThread_CyclicBarrier {
    public static void main(String[] args) {
        // 定義柵欄,並設置柵欄須要等待的線程數爲6
        CyclicBarrier barrier = new CyclicBarrier(6);
        int NUM = 100;
        for(int i=1; i<=NUM; i++){
            final int k = i;
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        Thread.sleep(k * 1000);
                        System.out.println("ThreadNo:"+k+" is waiting, getNumberWaiting:" + barrier.getNumberWaiting());
                        // 柵欄設置的等待線程數爲6,當線程數不夠6個時,全部線程將在此等待
                        barrier.await();
                        // 當線程數達到6個時,柵欄將被打開,全部線程都將被喚醒
                        System.out.println("ThreadNo:"+k+" is notify");
                        // 柵欄被重置,以便下次繼續使用
                        barrier.reset();
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
    }
}

五、信號量的方式get

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

/**
 * 信號量的使用
 * 信號量用於控制同時訪問某個資源的線程數量,信號量還能夠用於實現某個資源池。
 * 信號量管理者一組虛擬的許可,線程在執行操做時首先要得到許可,若是信號量的許可數量爲0,那麼accquire將阻塞直到有許可爲止
 * 信號量不是一次性的,當信號鏈的許可用完以後,能夠經過release釋放許可
 */
public class MutiThread_Semaphore {
    public static void main(String[] args) {
        // 定義信號量,並設置信號量的容許發放的最大許可數量爲6
        final Semaphore semaphore = new Semaphore(6);
        // 定義集合,當信號量未發放的許可數量大於0則容許線程向集合內添加元素
        final List<String> set = new ArrayList<>();
        int i = 1;
        while(true){
            final int k = i++;
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    boolean res = false;
                    try{
                        System.out.println("ThreadNo:A"+k+", availablePermits:"+semaphore.availablePermits());
                        // 當信號量容許發放的許可數量大於0,則會向集合內添加元素,不然將被中斷於此
                        semaphore.acquire();
                        res = set.add("1");
                        System.out.println("ThreadNo:A"+k+" add item success");
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }finally{
                        if(!res){
                            semaphore.release();
                        }
                    }
                }
            });
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    if(semaphore.availablePermits() == 0){
                        // 若是信號量容許發放的許可數量等於0,則釋放制定數量的許可
                        semaphore.release(3); //釋放3個許可
                        System.out.println("ThreadNo:B"+k+" releasePermitNum:"+semaphore.availablePermits());
                    }
                }
            });
            t.start();
            t2.start();
            System.out.println("the num of set:"+set.size());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

A、join的使用it

/**
 * join的使用
 * 實現當調用join的線程執行完畢後,其餘線程才能執行
 */
public class MutiThread_Join {
    public static void main(String[] args) {
        Thread a = new Thread(new Runnable(){
            @Override
            public void run(){
                printNumber("A");
            }
        });
        Thread b = new Thread(new Runnable(){
            @Override
            public void run(){
                printNumber("B");
            }
        });
        try{
            a.start();
            // a線程執行完畢後,b線程才能執行
            a.join();
            b.start();
        }catch(InterruptedException e){
            e.printStackTrace();;
        }
    }
    public static void printNumber(String s){
        System.out.println(s+" print:"+s);
    }
}

B、yield的使用io

/**
 * yield,當一個線程中調用了這個方法後,這個線程就會把本身的CPU執行時間讓給本身或其它線程,
 * 注意是讓給本身或其它線程,並非單純讓給其餘線程。yield執行後,能讓當前線程由運行狀態
 * 進入到就緒狀態,將本身的CPU時間片讓出來,讓出來以後有多是其它線程執行,也有多是該線程
 * 繼續執行。優先級高的線程並不必定是首先執行,而是首先執行的機率會高一些。優先級在大量線程
 * 執行的時候才能體現的出來。
 */
public class MutiThread_yield {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<10;i++){
                    System.out.println("ThreadNo:A"+i);
                    Thread.yield();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<10;i++){
                    System.out.println("ThreadNo:B"+i);
                    Thread.yield();
                }
            }
        });
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
    }
}
相關文章
相關標籤/搜索