架构系列——线程通信的实现方式

目录

一、什么是线程通信?

二、线程通信的实现

1.使用volatile实现线程通信

2.使用wait/notify实现线程通信

3.使用ReentrantLock创建Condition实现

4.通过LockSupport实现线程通信

5.使用CountDownLatch实现线程通信


一、什么是线程通信?

线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能称为一个整体,线程间的通信就成为整体的必用方式之一。

当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同时,还会使开发人员在处理线程任务的过程中有效的把控和监督。

二、线程通信的实现

1.使用volatile实现线程通信

如下列代码所示,list使用volatile修饰,利用volatile的可见性,当线程t1添加了5个元素的时候,t2得到通知!

弊端:需要不断地做while循环,影响性能!

public class ThreadCommunication {
	private volatile static List list = new ArrayList();
	
	public static void main(String[] args) {
		final ThreadCommunication ThreadCommunication = new ThreadCommunication();
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				try {
					for (int i = 0; i < 10; i++) {
						list.add("test");
						System.out.println("当前线程" + Thread.currentThread().getName() + "添加了一个元素");
						Thread.sleep(500);
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}, "t1");
		
		Thread t2 = new Thread(new Runnable(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					if(list.size() == 5){
						System.out.println("当前线程" + Thread.currentThread().getName() + "收到通知,list数量为5");
						throw new RuntimeException();
					}
				}
			}
			
		}, "t2");
		
		t2.start();
		t1.start();
	}
}

2.使用wait/notify实现线程通信

wait和notify都是Object类的方法,而Object是所有类的父类,所以,java为所有的对象提供了这两个方法。

1、wait和notify必须配合synchronized使用
2、wait方法释放锁,notify方法不释放锁

如下列代码所示,使用wait、notify结合synchronized实现线程通信。

弊端:不能实时获得锁,下面的代码运行之后,t1的循环做完t2才得到通知。

public class ThreadCommunication2 {
	private static List list = new ArrayList();
	
	public static void main(String[] args) {
		final Object lock = new Object();
		
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				try {
					synchronized(lock){
						for (int i = 0; i < 10; i++) {
							list.add("test");
							System.out.println("当前线程" + Thread.currentThread().getName() + "添加了一个元素");
							Thread.sleep(500);
							if(list.size() == 5){
								System.out.println("已经发出通知");
								lock.notify();
							}
						}
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}, "t1");
		
		Thread t2 = new Thread(new Runnable(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				synchronized(lock){
					if(list.size() != 5){
						try {
							lock.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					System.out.println("当前线程" + Thread.currentThread().getName() + "收到通知,list数量为5");
					throw new RuntimeException();
				}
			}
			
		}, "t2");
		
		t2.start();
		t1.start();
	}
}

3.使用ReentrantLock创建Condition实现

Conditon中的await()对应Object的wait()

Condition中的signal()对应Object的notify()

Condition中的signalAll()对应Object的notifyAll()

这种方法跟 Object 的 wait和 notify 一样,不能实时获得锁,下面的代码运行之后,t1的循环做完t2才得到通知。

public class ThreadCommunication3 {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        List<String> list = new ArrayList<>();

        Thread t1 = new Thread(() -> {
            lock.lock();
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程1向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5){
                    condition.signal();
                }

            }
            lock.unlock();
        });
        // 实现线程B
        Thread t2 = new Thread(() -> {
            lock.lock();
            if (list.size() != 5) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程2收到通知...");
            lock.unlock();
        });
        t2.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.start();
    }
}

4.通过LockSupport实现线程通信

LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,当然阻塞之后肯定得有唤醒的方法。

线程通信主要用到两个函数:

park:英文意思为停车。我们如果把Thread看成一辆车的话,park就是让车停下

unpark:就是让车启动然后跑起来 

弊端:需要知道被唤起的线程名字

public class ThreadCommunication4 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();

		final Thread t2 = new Thread(() -> {
			if (list.size() != 5) {
				//线程阻塞
				LockSupport.park();
			}
			System.out.println("线程2收到通知...");
		});
		Thread t1 = new Thread(() -> {
			for (int i = 1; i <= 10; i++) {
				list.add("abc");
				System.out.println("线程1向list中添加一个元素,此时list中的元素个数为:" + list.size());
				try {
					Thread.sleep(100);
				} catch (Exception e) {
					e.printStackTrace();
				}
				if (list.size() == 5){
					//唤起线程t2
					LockSupport.unpark(t2);
				}
			}
		});
		t1.start();
		t2.start();
	}
}

5.使用CountDownLatch实现线程通信

CountDownLatch是concurrent包里面的一个类,CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。当每一个线程完成自己任务后,计数器的值就会减1。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以唤醒其他线程,继续执行接下来的任务。

await()函数:此函数将会使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。

countDown函数:此函数将递减锁存器的计数,如果计数到达零,则释放所有等待的线程

如下列代码所示,t1中使用countDown方法之后,计数器会减到0,这时会立即唤醒t2线程,然后继续执行t2线程的代码

public class ThreadCommunication5 {
	private static List list = new ArrayList();
	
	public static void main(String[] args) {
		final CountDownLatch cdl = new CountDownLatch(1);//设置一个计数器
		
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				try {
					for (int i = 0; i < 10; i++) {
						list.add("test");
						System.out.println("当前线程" + Thread.currentThread().getName() + "添加了一个元素");
						Thread.sleep(500);
						if(list.size() == 5){
							System.out.println("已经发出通知");
							cdl.countDown();//计数器变为0,立即唤醒t2线程							
						}
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		}, "t1");
		
		Thread t2 = new Thread(new Runnable(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				if(list.size() != 5){
					try {
						cdl.await();//在计数器等于0之前,会一直处于等待状态	
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("当前线程" + Thread.currentThread().getName() + "收到通知,list数量为5");
				throw new RuntimeException();
			}
		}, "t2");
		
		t2.start();
		t1.start();
	}
}

 

参考文献:

[1].CountDownLatch

[2].java中Condition类的详细介绍

[3].LockSupport的用法及原理

[4].什么时候用CountDownLatch?

posted @ 2021-08-28 10:36  前方一片光明  阅读(70)  评论(0编辑  收藏  举报