Java线程通信的多种实现形式(代码为主)
来源:互联网 发布:js设置按钮隐藏 编辑:程序博客网 时间:2024/06/16 16:25
1.1 概念
使用wait和notify方法实现线程间的通信(这两个方法都是Object的类的方法)
wait和notify必须配合synchronized关键字使用
wait方法释放锁,而notify方法不释放锁.
1.2 线程通信实例1—线程通信基本版
比较简单的一个线程间通信的例子:
线程1负责向list中添加元素,线程2判断list中的元素的个数,当等于5的时候,线程2终止;
1.2.1 代码
/*
* Description:线程通信01
* fileName:ListAdd1.java
* author:@xflig
* Date: 2017年11月26日下午3:32:32
*
* Version: @version1.0
*
*/
public class ListAdd1
{
private volatile static List list = new ArrayList<>();
public void add()
{
list.add("apple");
}
public int size()
{
returnlist.size();
}
public static void main(String[] args)
{
//1.创建对象
final ListAdd1listAdd1 = new ListAdd1();
//2 创建2个线程
Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
for(inti = 0; i < 10;i++)
{
listAdd1.add();
System.out.println("当前线程" + Thread.currentThread().getName()+"新添加了一个元素..." );
Thread.sleep(2000);
}
}
catch (Exceptione)
{
}
}
},"t1");
Thread t2 = new Thread(new Runnable()
{
@Override
public void run()
{
while (true)
{
if(listAdd1.size() == 5)
{
System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + "list 长度为5,线程停止....");
thrownew RuntimeException();
}
}
}
},"t2");
t1.start();
t2.start();
}
}
1.2.2 运行结果
弊端:线程2一致在判断当前list的size是否为5,消耗内存.
1.3 线程通信实例2—使用wait/notify线程通信
1.3.1 代码
/*
* Description:线程通信--wait/notify
* fileName:ListAdd2.java
* author:@xflig
* Date: 2017年11月26日下午3:57:44
*
* Version: @version1.0
*
*/
public class ListAdd2
{
private volatile static List list = new ArrayList<>();
public void add()
{
list.add("orange");
}
public int size()
{
returnlist.size();
}
public static void main(String[] args)
{
//创建对象
final ListAdd2listAdd2 = new ListAdd2();
//实例出来一个lock
final Objectlock = new Object();
//当时用wait/notify的时候,一定要配合synchronized使用
//创建2个线程
Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
synchronized (lock)
{
for(inti=0; i<10; i++)
{
listAdd2.add();
System.out.println("当前线程" + Thread.currentThread().getName()+"新添加了一个元素..." );
Thread.sleep(1000);
if(listAdd2.size() == 5)
{
System.out.println(Thread.currentThread().getName() +"发出通知....");
lock.notify();
}
}
}
}
catch (Exceptione)
{
e.printStackTrace();
}
}
},"t1");
Thread t2 = new Thread(new Runnable()
{
@Override
public void run()
{
synchronized (lock)
{
if(listAdd2.size() != 5)
{
try
{
lock.wait();
}
catch (InterruptedExceptione)
{
e.printStackTrace();
}
}
System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + "list 长度为5,线程停止....");
thrownew RuntimeException();
}
}
},"t2");
//启动线程
t2.start();
t1.start();
}
}
注意:
启动线程的时候,我们一定要先启动线程2,保证线程2先拿到锁,然后判断list的长度是否为5,如果不是5的话,则进入wait,wait是释放锁的,线程1拿到刚刚释放的锁,进行操作,当list的长度为5 的时候,发出notify,但是notify是不释放锁的,只有等待线程1执行结束后,才释放锁.
对比先启动线程1,查看运行结果
1.3.2 运行结果
我们看到,这样的设计,好处是:解决了基础版的线程2一直在循环的问题,但是又引入了新的问题,通知没有实时性.
1.4 线程通信实例3—使用countDownLatch 实现实时通信
1.4.1 代码
在实例2的基础上简单做修改
/*
* 使用countDownLatch实现实时通信
* Description: fileName:ListAdd3.java
* author:@xflig
* Date: 2017年11月26日下午4:32:55
* Version: @version1.0
*/
public class ListAdd3
{
private volatile static List list = new ArrayList<>();
public void add()
{
list.add("orange");
}
public int size()
{
returnlist.size();
}
public static void main(String[] args)
{
// 创建对象
final ListAdd2listAdd2 = new ListAdd2();
// 实例出来一个lock
// final Object lock = new Object();
final CountDownLatchcountDownLatch = new CountDownLatch(1);
// 当时用wait/notify的时候,一定要配合synchronized使用
// 创建2个线程
Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
// synchronized (lock)
// {
for (inti = 0; i < 10;i++ )
{
listAdd2.add();
System.out.println(
"当前线程" + Thread.currentThread().getName() +"新添加了一个元素...");
Thread.sleep(1000);
if (listAdd2.size() == 5)
{
System.out.println(Thread.currentThread().getName() +"发出通知....");
// lock.notify();
countDownLatch.countDown();
}
}
// }
}
catch (Exceptione)
{
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable()
{
@Override
public void run()
{
// synchronized (lock)
// {
if (listAdd2.size() != 5)
{
try
{
System.out.println("t2进入...");
countDownLatch.await();
// lock.wait();
}
catch (InterruptedExceptione)
{
e.printStackTrace();
}
}
System.out.println(
"当前线程收到通知:" + Thread.currentThread().getName() +"list 长度为5,线程停止....");
throw new RuntimeException();
// }
}
}, "t2");
// 启动线程
t2.start();
t1.start();
}
}
1.4.2 运行结果
1.5 实例3使用wait/notify模拟queue
1.5.1 概述
BlockingQueue;首先它是一个队列,并且支持队列中的阻塞,阻塞的放入和得到数据.我们要实现下面的两个简答的方法put和take
put(Object obj):把obj加到BlockingQueue里,如果BlockingQueue没有空间,则调用此方法的线程被阻塞,知道BlockingQueue里面由空间再继续;
take():取走BlockingQueue里排在首位的元素,若BlockingQueue里面没有元素,则调用此方法的线程阻塞,直到BlockingQueue有新的数据被加入.
1.5.2 代码
/*
* Description:使用wait/notify模拟队列
* fileName:MyQueue.java
* author:@xflig
* Date: 2017年11月26日下午4:47:45
*
* Version: @version1.0
*
*/
public class MyQueue
{
//1 创建一个装载元素的集合
private LinkedList<Object>list = new LinkedList<Object>();
//2 需要一个计数器
private AtomicIntegercount = new AtomicInteger(0);
//3 集合的上限和下限
private final int minSize = 0;
private final int maxSize;
//4 构造方法
public MyQueue(intlen)
{
this.maxSize =len;
}
//5 初始化一个对象用于加锁
private Objectlock = new Object();
//6 put(Object obj):把obj加到BlockingQueue里,如果BlockingQueue没有空间,则调用此方法的线程被阻塞,知道BlockingQueue里面由空间再继续;
public void put(Object obj)
{
synchronized (lock)
{
while(count.get() ==this.maxSize )
{
try
{
lock.wait();
}
catch (InterruptedExceptione)
{
e.printStackTrace();
}
}
list.add(obj);
//计数器+1
count.incrementAndGet();
System.out.println("新加入的元素为:" +obj);
lock.notify();
}
}
//7 take():取走BlockingQueue里排在首位的元素,若BlockingQueue里面没有元素,则调用此方法的线程阻塞,直到BlockingQueue有新的数据被加入.
public Object take()
{
Object result = null;
synchronized (lock)
{
while(count.get() ==this.minSize)
{
try
{
lock.wait();
}
catch (InterruptedExceptione)
{
e.printStackTrace();
}
}
//1 移除元素
result = list.removeFirst();
//2 计数器-1
count.decrementAndGet();
//3 唤醒操作
lock.notify();
}
returnresult;
}
public int getSize()
{
returnlist.size();
}
public static void main(String[] args)
{
final MyQueuemyQueue = new MyQueue(5);
//添加初始化元素
myQueue.put("a");
myQueue.put("b");
myQueue.put("c");
myQueue.put("d");
myQueue.put("e");
System.out.println("当前容器的元素个数为:" +myQueue.getSize());
//创建线程1--添加元素
Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
myQueue.put("f");
myQueue.put("g");
}
},"t1");
t1.start();
//创建线程2--移除元素
Thread t2 = new Thread(new Runnable()
{
@Override
public void run()
{
Object obj = myQueue.take();
System.out.println("移除的元素为:" +obj);
Object obj2 = myQueue.take();
System.out.println("移除的元素为:" +obj2);
}
},"t2");
try
{
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedExceptione)
{
e.printStackTrace();
}
t2.start();
}
}
1.5.3 运行结果
- Java线程通信的多种实现形式(代码为主)
- 用JAVA实现文本形式的树状结构显示代码
- java线程两种实现形式
- 线程之间进程之间的通信方式及其代码实现
- Java 线程经典问题,三个线程,循环打印ABCABCABC 的多种实现方法
- java的线程通信
- 线程通信的参考代码
- 线程的两种实现形式
- 多种数据结构的Java实现
- 判断是否为主线程的方法
- Java线程代码实现
- 多种形式的for循环
- 多种形式的ListView案例
- 输出分数的多种形式
- java 简单线程池的实现代码
- java线程间通信[实现不同线程之间的消息传递(通信),生产者和消费者模型]
- Java线程间通信-回调的实现方式
- Java线程间通信-回调的实现方式
- [leetcode]#119. Pascal's Triangle II
- Linux操作系统与实训教程(习题1)
- Codeforce 894B Ralph And His Magic Field (思维+快速幂)
- _caffe.so: undefined symbol: _ZN5caffe4mtx_E
- 记一次说走就走的旅行
- Java线程通信的多种实现形式(代码为主)
- Lambda表达式与简洁代码
- Java-day01
- iOS截屏后仿今日头条实现一键分享
- Docker使用记录
- 操作系统-循环首次适应算法
- 3个月可以做什么
- ssm 项目遇到mapper 里循环两次取参数的问题
- Pat 1028. 人口普查(20)