涉及内容:
日志:
总结:
- 控制并发获取资源
- 控制并发获取多份copy资源
- 等待多个并发事件
- 在公共点同步任务
- 运行并发多阶段任务
- 在阶段任务中控制阶段变化
- 在并发任务之间改变数据
1、运行并发多阶段任务
如果你玩过恐龙快打,这个有点类似,就是两个用户必须要同时打完这个关然后一起进入下一关,否则会一直在那里等着。
例子:删除超过24小时的日志文件,分为三步:
- 1、得到以.log为后缀的所有文件
- 2、判断创建或修改时间是否超过24
- 3、打印结果到控制台
package com.jack;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
public class FileSearch implements Runnable{
private String initPath;
private String end;
private List<String> results;
private Phaser phaser;
public FileSearch(String initPath, String end, Phaser phaser) {
super();
this.initPath = initPath;
this.end = end;
this.phaser = phaser;
results =new ArrayList<>();
}
private void directoryProcess(File file){
File list[] = file.listFiles();
if(list != null){
for (int i=0; i< list.length; i++){
if(list[i].isDirectory()){
directoryProcess(list[i]);
}else {
fileProcess(list[i]);
}
}
}
}
private void fileProcess(File file) {
if (file.getName().endsWith(end)) {
results.add(file.getAbsolutePath());
}
}
/**
* 过滤结果
*/
private void filterResults(){
List <String> newResults = new ArrayList<>();
long actualDate = new Date().getTime();
for (int i=0; i<results.size(); i++){
File file = new File(results.get(i));
long fileDate = file.lastModified();
if(actualDate-fileDate < TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS)){
newResults.add(results.get(i));
}
}
results = newResults;
}
/**
* 检查结果
* @return
*/
private boolean checkResults(){
if(results.isEmpty()) {
System.out.printf("%s:阶段 %d: 结果.\n", Thread.currentThread().getName(),
phaser.getPhase());
System.out.printf("%s: 阶段 %d :结束 \n", Thread.currentThread().getName(),phaser.getPhase());
phaser.arriveAndDeregister();
return false;
} else {
System.out.printf("%s: 阶段 %d:%d结果.\n", Thread.currentThread().getName(), phaser.getPhase(),results.size());
phaser.arriveAndAwaitAdvance();
return true;
}
}
/**
* 展示信息
*/
private void showInfo(){
for (int i=0; i<results.size(); i++){
File file = new File(results.get(i));
System.out.printf("%s: %s\n", Thread.currentThread().getName(), file.getAbsolutePath());
}
phaser.arriveAndAwaitAdvance();
}
@Override
public void run() {
phaser.arriveAndAwaitAdvance();
System.out.printf("%s : 开始\n", Thread.currentThread().getName());
File file = new File(initPath);
if(file.isDirectory()){
directoryProcess(file);
}
if(!checkResults()){
return;
}
filterResults();
if(!checkResults()){
return;
}
showInfo();
phaser.arriveAndDeregister();
System.out.printf("%s : 工作完成了\n", Thread.currentThread().getName());
}
}
package com.jack;
import java.util.concurrent.Phaser;
public class Main {
public static void main(String[] args) {
Phaser phaser = new Phaser(2);
FileSearch system = new FileSearch("C:\\Windows", "log", phaser);
FileSearch apps = new FileSearch("C:\\Program Files", "log", phaser);
//FileSearch documents= new FileSearch("C:\\Documents And Settings","log", phaser);
Thread systemThread = new Thread(system, "System");
systemThread.start();
Thread appsThread = new Thread(apps, "Apps");
appsThread.start();
//Thread documentsThread = new Thread(documents, "Documents");
try {
systemThread.join();
appsThread.join();
//documentsThread.join();
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.printf("终止了:" , phaser.isTerminated());
}
}
日志:
System : 开始
Apps : 开始
Apps: 阶段 1:2结果.
System: 阶段 1:92结果.
Apps:阶段 2: 0结果.
Apps: 阶段 2 :结束
System: 阶段 2:9结果.
System: C:\Windows\inf\setupapi.app.log
System: C:\Windows\Logs\DISM\dism.log
System: C:\Windows\setupact.log
System: C:\Windows\SoftwareDistribution\DataStore\Logs\edb.log
System: C:\Windows\System32\catroot2\edb.log
System: C:\Windows\SysWOW64\Macromed\Flash\FlashInstall32.log
System: C:\Windows\temp\LDSGameCenter\Log\GmSvc.log
System: C:\Windows\temp\Tencent\log_09-21-22_2032.log
System: C:\Windows\WindowsUpdate.log
System : 工作完成了
终止了:
总结:
- 1、arriveAndDeregister()表示提前结束,不用等待
- 2、arriveAndAwaitAdvance()表示等待,不会提前结束
- 3、在创建的时候new Phaser(2); 两个阶段(等待两个线程完成)
Phaser有两个状态
Active:激活状态
Termination:终止状态
2、在并发阶段任务控制阶段的变化
Phaser提供一个方法每执行一次改变阶段状态,这个方法就是onAdvance(),它有两个参数:当前的阶段(为int类型)和执行注册的部分。它将返回一个boolean值,false表示继续执行,true表示执行完进入终止状态,我们可以重写这个方法,完成一些工作。
例子:模拟学生三次考试,在参加后一个考试之前必须通过前一个考试,类似,初中高级考试。
package com.jack;
import java.util.concurrent.Phaser;
public class MyPhaser extends Phaser {
/**
* 当phase=0 执行 studentsArrived方法
* 当phase=1 执行finishFirstExercise方法
* 当phase=2 执行 finishSecondExercise方法
* 当phase=3 执行 finishExam()方法
* 返回true表示终止
* (non-Javadoc)
* @see java.util.concurrent.Phaser#onAdvance(int, int)
*/
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch(phase){
case 0:
return studentsArrived();
case 1:
return finishFirstExercise();
case 2:
return finishSecondExercise();
case 3:
return finishExam();
default:
return true;
}
}
private boolean finishExam() {
System.out.printf("阶段: 所有学生已经完成所有考试\n");
System.out.printf("阶段: 谢谢你考试.\n");
return true;
}
private boolean finishSecondExercise() {
System.out.printf("阶段: 所有学生已经完成第二次考试\n");
System.out.printf("阶段:开始准备第三次考试\n");
return false;
}
private boolean finishFirstExercise() {
System.out.printf("阶段:所有学生已经完成第一次考试\n");
System.out.printf("阶段:开始准备第二次考试\n");
return false;
}
private boolean studentsArrived() {
System.out.printf("阶段:考试开始,学生准备好了\n");
System.out.printf("阶段:我们有 %d学生\n", getRegisteredParties());
return false;
}
}
package com.jack;
import java.util.Date;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
public class Student implements Runnable{
private Phaser phaser;
public Student(Phaser phaser) {
super();
this.phaser = phaser;
}
@Override
public void run() {
System.out.printf("%s: 已经到达考试点。%s\n", Thread.currentThread().getName(), new Date());
phaser.arriveAndAwaitAdvance();
System.out.printf("%s: 开始做第一次测试.%s\n", Thread.currentThread().getName(), new Date());
doExercise1();
System.out.printf("%s: 已经完成第一次测试。%s\n", Thread.currentThread().getName(), new Date());
phaser.arriveAndAwaitAdvance();
System.out.printf("%s: 开始做第二次测试 %s\n", Thread.currentThread().getName(), new Date());
doExercise2();
System.out.printf("%s: 已经完成第二次测试 %s\n", Thread.currentThread().getName(),new Date());
phaser.arriveAndAwaitAdvance();
System.out.printf("%s: 开始做第三次测试%s\n", Thread.currentThread().getName(), new Date());
doExercise3();
System.out.printf("%s: 完成测试 %s\n", Thread.currentThread().getName(), new Date());
phaser.arriveAndAwaitAdvance();
}
private void doExercise3() {
try {
long duration=(long)(Math.random()*10);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void doExercise2() {
try {
long duration=(long)(Math.random()*10);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void doExercise1() {
try {
long duration=(long)(Math.random()*10);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.jack;
public class Main {
public static void main(String[] args) {
MyPhaser phaser = new MyPhaser();
Student students[] = new Student[5];
for (int i=0; i<students.length; i++){
students[i] = new Student(phaser);
phaser.register();
}
Thread threads[] = new Thread[students.length];
for (int i=0; i<students.length; i++){
threads[i] = new Thread(students[i], "Student "+i);
threads[i].start();
}
for (int i=0; i<threads.length; i++){
try{
threads[i].join();
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.printf("Main : 该阶段已经完成了: %s.\n", phaser.isTerminated());
}
}
日志:
总结:
- 1、注入五个student,开启5个线程。
- 2、重写Phaser,可以自定义多个阶段。这是定义4个阶段,默认从0开始。
3、在并发任务改变数据
线程中间数据交换
例子:采用Exchanger去解决生产者和消费者模型。
package com.jack;
import java.util.List;
import java.util.concurrent.Exchanger;
public class Producer implements Runnable{
private List<String> buffer;
private final Exchanger<List<String>> exchanger;
public Producer(List<String> buffer, Exchanger<List<String>> exchanger) {
super();
this.buffer = buffer;
this.exchanger = exchanger;
}
@Override
public void run() {
int cycle =1;
for(int i=0; i<10; i++){
System.out.printf("生产者: Cycle %d\n", cycle);
for (int j=0; j<10; j++){
String message = "事件 " + ((i*10) +j);
System.out.printf("生产者:%s\n", message);
buffer.add(message);
}
try {
buffer = exchanger.exchange(buffer);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.printf("生产者: "+buffer.size());
cycle++;
}
}
}
package com.jack;
import java.util.List;
import java.util.concurrent.Exchanger;
public class Consumer implements Runnable{
private List<String> buffer;
private final Exchanger<List<String>> exchanger;
public Consumer(List<String> buffer, Exchanger<List<String>> exchanger) {
super();
this.buffer = buffer;
this.exchanger = exchanger;
}
@Override
public void run() {
int cycle =1;
for(int i=0; i<10; i++){
System.out.printf("消费者: Cycle%d \n", cycle);
try {
buffer = exchanger.exchange(buffer);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.printf("消费者--缓存大小:%d \n",buffer.size());
for(int j=0; j<10; j++){
String message = buffer.get(0);
System.out.printf("消费者--信息: %s\n",message);
buffer.remove(0);
}
cycle++;
}
}
}
package com.jack;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Exchanger;
public class Main {
public static void main(String[] args) {
List<String> buffer1 = new ArrayList<>();
List<String> buffer2 = new ArrayList<>();
Exchanger<List<String>> exchanger =new Exchanger<>();
Producer producer = new Producer(buffer1, exchanger);
Consumer consumer = new Consumer(buffer2, exchanger);
Thread threadProducer = new Thread(producer);
Thread threadConsumer = new Thread(consumer);
threadProducer.start();
threadConsumer.start();
}
}
总结:
- 1、这个类似4x100百米接力赛,生产者将会生成的数据通过exchanger方法转换给消费者,(exchanger就是接力棒)
- 2、buffer = exchanger.exchange(buffer); 每生成10个交换一次。