Java实现线程通信的几种方式

本文共总结了5种方式,通过代码举例的方式进行展示。5种方式为:synchronized加wait/notify方式、ReentrantLock加Condition方式、闭锁的方式、栅栏的方式、信号量的方式。

最后还介绍了join和yield的使用。

1、synchronized加wait/notify方式

/**
 * 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();
    }
}

2、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();
        }
    }
}

3、闭锁方式

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();
        }
    }
}

4、栅栏的方式

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();
        }
    }
}

5、信号量的方式

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的使用

/**
 * 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的使用

/**
 * 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();
    }
}

转载于:https://my.oschina.net/u/3272058/blog/3068524

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值