Java核心知识点学习----多线程 倒计时记数器CountDownLatch和数据交换的Exchanger

本文将要介绍的内容都是Java5中的新特性,一个是倒计时记数器---CountDownLatch,另一个是用于线程间数据交换的Exchanger.

一.CountDownLatch


1.什么是CountDownLatch?

倒计时计数器,调用CountDownLatch对象的CountDown()方法就将计数器减一,当计数到达0时,则所有等待者或者全部等待者开始执行.

2.如何用?

new CountDownLatch(1);

直接new,其构造函数必须传一个int类型的参数,参数的意思是:

count the number of times countDown must be invoked before threads can pass through await

大致可理解成,有一个门,有N个门闩,要想打开门必须把所有门闩都打开,对应到线程上是说在线程通过等待前必须要执行的倒计时操作.

3.举例


package com.amos.concurrent;

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* @ClassName: Count_Down_Latch_Test
* @Description: 倒计时学习
* @author: amosli
* @email:[email protected]
* @date Apr 27, 2014 11:51:43 PM
*/
public class Count_Down_Latch_Test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
final CountDownLatch countdownOrder = new CountDownLatch(1);// an order
final CountDownLatch countdownAnwser = new CountDownLatch(3);// anwser
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
countdownOrder.await();
System.out.println("线程" + Thread.currentThread().getName() + " 正准备接受命令");
System.out.println("线程"+Thread.currentThread().getName()+" 已经接受命令!");
Thread.sleep(new Random().nextInt(1000));
System.out.println("线程"+Thread.currentThread().getName()+" 回应处理结果!");
countdownAnwser.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
executorService.execute(runnable);//启动线程池
}

try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("线程"+Thread.currentThread().getName()+" 即将下达命令!!");
countdownOrder.countDown();
System.out.println("线程"+Thread.currentThread().getName()+" 已经下达命令,正在等待返回结果!");
countdownAnwser.await();
System.out.println("线程"+Thread.currentThread().getName()+" 已经收到所有处理结果!");

} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

1).效果如下图所示:

2)程序说明

首先是创建了一个可缓存的线程池--->接着,创建两个CountDownLatch类,一个赋值为1,一个赋值为3;----->然后,执行一个for循环,在循环中,首先是实现了一个Runnable接口,然后,将Runnable接口加入到线程池中;
其中Runnable接口,首先是等待计数器为0,然后如果为0那么将计数器2的值减一,每循环一次减一,当第三次循环时,线程执行完毕;----->在Runnable接口中等待计数器为0,整个程序无法向下走,这时main方法,即主线程执行CountDown方法,计数器减一-------->最后等待所有的线程都执行完毕,返回最终的结果.

4.扩展--官方例子

package com.amos.concurrent;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {
public static void main(String[] args) {
try {
new CountDownLatchTest().new Driver().main();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class Driver { // ...
void main() throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(3);

for (int i = 0; i < 3; ++i)
// create and start threads
new Thread(new worker(startSignal, doneSignal)).start();
dosomethingelse(); // don‘t let run yet
startSignal.countDown(); // let all threads proceed
dosomethingelse();
doneSignal.await(); // wait for all to finish
}

private void dosomethingelse() {
System.out.println("dosomethingelse...");
}
}

class worker implements Runnable {
private final CountDownLatch startsignal;
private final CountDownLatch donesignal;

worker(CountDownLatch startsignal, CountDownLatch donesignal) {
this.startsignal = startsignal;
this.donesignal = donesignal;
}

public void run() {
try {
startsignal.await();
dowork();
donesignal.countDown();
} catch (Exception ex) {
} // return;
}

void dowork() {
System.out.println("dowork....");
}
}

}

跟上面例子差不多,首先都是设置一个等待,然后再调用计数器减一,执行最后的操作.

CountDownLatch很适用于跑步比赛,当发令枪一声令下,所有选手开始跑起来.

二.Exchanger


1.什么是Exchange?作用是什么?


用于实现两人之间的数据交换,每个人在完成一定的事务后想与对方交换数据;只有两人见面才会有交换.就像是情人间的约会,不见不散.

2.如何使用?

new Exchanger<V>();

这里用到了泛型,即可以指定任意格式的数据,基本类型,对象等等都可以.

这里要注意的是线程要成对出现才能进行数据交换.用来交换的方法为exchange(x); 

Parameters:
x the object to exchange

参数为要进行交换给对方的数据.

3.举例:


package com.amos.concurrent;

import java.util.Random;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* @ClassName: ExchangerTest
* @Description: 线程间的数据交换Exchanger
* @author: amosli
* @email:[email protected]
* @date Apr 28, 2014 12:26:48 AM
*/
public class ExchangerTest {
public static void main(String[] args) {
final Exchanger<String> exchanger = new Exchanger<String>();

ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();

//线程一
newCachedThreadPool.execute(new Runnable() {
public void run() {
try {
String data1="111";
System.out.println("线程:"+Thread.currentThread().getName()+" 要换出去的数据为:"+data1);
Thread.sleep(new Random().nextInt(1000));
String exchange = exchanger.exchange(data1);
System.out.println("线程:"+Thread.currentThread().getName()+" 换回来的数据为:"+exchange);
} catch (InterruptedException e) {
e.printStackTrace();
}

}
});

//线程二
newCachedThreadPool.execute(new Runnable() {
public void run() {
try {
String data1="hi_amos";
System.out.println("线程:"+Thread.currentThread().getName()+" 要换出去的数据为:"+data1);
Thread.sleep(new Random().nextInt(1000));
String exchange = exchanger.exchange(data1);
System.out.println("线程:"+Thread.currentThread().getName()+" 换回来的数据为:"+exchange);
} catch (InterruptedException e) {
e.printStackTrace();
}

}
});
}
}

这里只需要注意使用exchange()方法即可.

效果:

4.扩展---官方例子


class FillAndEmpty {
Exchanger exchanger = new Exchanger();
DataBuffer initialEmptyBuffer = ... a made-up type
DataBuffer initialFullBuffer = ...

class FillingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialEmptyBuffer;
try {
while (currentBuffer != null) {
addToBuffer(currentBuffer);
if (currentBuffer.isFull())
currentBuffer = exchanger.exchange(currentBuffer);
}
} catch (InterruptedException ex) { ... handle ... }
}
}

class EmptyingLoop implements Runnable {
public void run() {
DataBuffer currentBuffer = initialFullBuffer;
try {
while (currentBuffer != null) {
takeFromBuffer(currentBuffer);
if (currentBuffer.isEmpty())
currentBuffer = exchanger.exchange(currentBuffer);
}
} catch (InterruptedException ex) { ... handle ...}
}
}

void start() {
new Thread(new FillingLoop()).start();
new Thread(new EmptyingLoop()).start();
}
}
}

官方的例子,也比较简单,启动两个线程,然后调用exchange()方法进行两个线程间的数据交换.

Java核心知识点学习----多线程 倒计时记数器CountDownLatch和数据交换的Exchanger,布布扣,bubuko.com

时间: 2024-12-28 11:49:54

Java核心知识点学习----多线程 倒计时记数器CountDownLatch和数据交换的Exchanger的相关文章

Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过start方法启动线程--->线程变为可运行可执行状态,然后通过数据产生共享,线程产生互斥---->线程状态变为阻塞状态---->阻塞状态想打开的话可以调用notify方法. 这里Java5中提供了封装好的类,可以直接调用然后构造阻塞状态,以保证数据的原子性. 2.如何实现? 主要是实现Blo

java中的线程协调与线程间的数据交换

Java中提供多种方式协调线程的工作. CountDownLatch:当多个也就是具体的数量等于CountDownLatch初始化参数的时候.线程达到了预期状态或者完成了预期工作时触发事件,其他线程可以等待这个事件来触发自己的后续工作.等待的线程是多个.达到了预期状态的线程会调用CountDownLatch的countDown方法.等待的线程会调用CountDownLatch的await方法. import java.util.concurrent.CountDownLatch; import

java核心知识点 --- 线程池ThreadPool

线程池是多线程学习中需要重点掌握的. 系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互.在这种情形下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池. 一.如何创建线程池?? 在Java5之前,线程池都是开发才手动实现的,从Java5开始,Java内建支持线程池.主要是新增了一个executors工厂类来生产线程池. 1.newCachedThreadPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓

Java核心知识点-NIO

文件读取中的NIO 在Java1.4之前的I/O系统中,提供的都是面向流的I/O系统,系统一次一个字节地处理数据,一个输入流产生一个字节的数据,一个输出流消费一个字节的数据,面向流的I/O速度非常慢,而在Java 1.4中推出了NIO,这是一个面向块的I/O系统,系统以块的方式处理处理,每一个操作在一步中产生或者消费一个数据库,按块处理要比按字节处理数据快的多. 在NIO中有几个核心对象需要掌握:缓冲区(Buffer).通道(Channel).选择器(Selector). 缓冲区Buffer 缓

Java核心知识点-字节流和字符流详解

字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢? 区别:实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件,如图12-6所示. 下面以两个写文件的操作为主进行比较,但是在操作时字节流和字符流的操作完成之后都不关闭输出流. 范例:使用字节流不关闭执行 Java代码   package org.lxh.demo12.byteiodemo; import java.io.File; import 

Java核心知识点-ClassLoader详解

ClassLoader ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回这个类的class对象. 1.1 几个相关概念ClassLoader负责载入系统的所有Resources(Class,文件,来自网络的字节流等),通过ClassLoader从而将资源载入JVM 每个class都有一个reference,指向自己的ClassLoader.Class.getClassLoader() array的Cl

Java核心_Git_Maven学习[20181108]

一.Git课程 ->Git概念 Git是一个开源的分布式版本控制系统,可以有效.高速的处理从很小到非常大的项目版本管理. [1]  Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件. Git --- The stupid content tracker, 傻瓜内容跟踪器.Linus Torvalds 是这样给我们介绍 Git 的. Git 是用于 Linux内核开发的版本控制工具.与常用的版本控制工具 CVS, Subversion

Java核心知识点

4.1.1. JAVA 并发知识库 4.1.2. JAVA 线程实现/创建方式 4.1.2.1. 继承 Thread 类 Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例.启动线程的唯一方 法就是通过 Thread 类的 start()实例方法.start()方法是一个 native 方法,它将启动一个新线 程,并执行 run()方法. 4.1.2.2. 实现 Runnable 接口. 如果自己的类已经 extends 另一个类,就无法直接 extends T

Java核心知识点-JVM结构之常量池

触摸java常量池 java常量池是一个经久不衰的话题,也是面试官的最爱,题目花样百出,小菜早就对常量池有所耳闻,这次好好总结一下. 理论 小菜先拙劣的表达一下jvm虚拟内存分布: 程序计数器是jvm执行程序的流水线,存放一些跳转指令,这个太高深,小菜不懂. 本地方法栈是jvm调用操作系统方法所使用的栈. 虚拟机栈是jvm执行java代码所使用的栈. 方法区存放了一些常量.静态变量.类信息等,可以理解成class文件在内存中的存放位置. 虚拟机堆是jvm执行java代码所使用的堆. Java中的