第三章 Thread Synchronization Utilities(线程同步工具类)【下】

涉及内容:
  • 控制并发获取资源
  • 控制并发获取多份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个交换一次。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值