第七章 Customizing Concurrency Classes(自定义并发类)【上】

本章涉及内容:

  • 自定义ThreadPoolExecutor类
  • 实现基于优先级Executor类
  • 实现ThreadFactory接口生成自定义线程
  • 在Executor对象中使用自定义ThreadFactory
  • 在计划线程池运行自定义任务
  • 通过实现ThreadFactory接口为Fork/Join框架生成自定义线程
  • 在Fork/Join框架运行自定义任务
  • 实现自定义锁类
  • 基于优先级实现传输队列
  • 实现自己的原子对象

简介:

当现有并发类不符合要求,可以自定义并发类。

1、自定义ThreadPoolExecutor

例子:重写ThreadPoolExecutor类去计算执行任务的时间

package com.jack;

import java.util.Date;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyExecutor extends ThreadPoolExecutor{
	
	private ConcurrentHashMap<String, Date> startTime;

	public MyExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
		startTime = new ConcurrentHashMap<>();
	}

	@Override
	public void shutdown() {
		System.out.printf("MyExecutor: shutdown开始关闭.\n");
		System.out.printf("MyExecutor: 执行完的任务的数量:%d\n", getCompletedTaskCount());
		System.out.printf("MyExecutor: 运行着的任务数量:%d\n", getActiveCount());
		System.out.printf("MyExecutor: 挂起任务数量:%d\n", getQueue().size());
		super.shutdown();
	}

	@Override
	public List<Runnable> shutdownNow() {
		System.out.printf("MyExecutor: shutdownNow马上关闭.\n");
		System.out.printf("MyExecutor: 执行完的任务的数量:%d\n", getCompletedTaskCount());
		System.out.printf("MyExecutor: 运行着的任务数量:%d\n", getActiveCount());
		System.out.printf("MyExecutor: 挂起任务数量:%d\n", getQueue().size());
		return super.shutdownNow();
	}

	@Override
	protected void beforeExecute(Thread t, Runnable r) {
		System.out.printf("MyExecutor: beforeExecute一个任务已经开始了:%s : %s\n", t.getName(), r.hashCode());
		startTime.put(String.valueOf(r.hashCode()), new Date());
		super.beforeExecute(t, r);
	}

	@Override
	protected void afterExecute(Runnable r, Throwable t) {
		Future<?> result = (Future<?>)r;
		try{
			System.out.printf("************************\n");
			System.out.printf("MyExecutor:afterExecute 一个任务已经完成了\n");
			System.out.printf("MyExecutor: 结果: %s\n", result.get());
			Date startDate = startTime.remove(String.valueOf(r.hashCode()));
			Date finishDate = new Date();
			long diff = finishDate.getTime() - startDate.getTime();
			System.out.printf("MyExecutor: 执行的时间为%d\n", diff);
			System.out.printf("**************************\n");
		} catch (InterruptedException | ExecutionException e){
			e.printStackTrace();
		}
		super.afterExecute(r, t);
	}
	
}

总结:

  • 1、继承ThreadPoolExecutor,重写shutdown() ,beforeExecute()等等方法,增加日志
  • 2、增加一个并发存储启动时间。

package com.jack;

import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

public class SleepTwoSecondsTask implements Callable<String>{

	@Override
	public String call() throws Exception {
		TimeUnit.SECONDS.sleep(2);
		return new Date().toString();
	}

}

package com.jack;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

public class Main {
	public static void main(String[] args) throws Exception{
		MyExecutor myExecutor = new MyExecutor(2, 4, 1000, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<Runnable>());
		List<Future<String>> results = new ArrayList<>();
		for(int i=0; i<10;i++){
			SleepTwoSecondsTask task = new SleepTwoSecondsTask();
			Future<String> result = myExecutor.submit(task);
			results.add(result);
		}
		for(int i=0; i<5; i++){
			try {
				String result = results.get(i).get();
				System.out.printf("Main: 关闭=前=Task的结果: %d: %s\n", i, result);
				
			}catch(InterruptedException|ExecutionException e){
				e.printStackTrace();
			}
		}
		myExecutor.shutdown();
		for(int i=0; i<5; i++){
			try {
				String result = results.get(i).get();
				System.out.printf("Main: 关闭=后=Task的结果: %d: %s\n", i, result);
				
			}catch(InterruptedException|ExecutionException e){
				e.printStackTrace();
			}
		}
		try{
			myExecutor.awaitTermination(1, TimeUnit.DAYS);
		} catch (InterruptedException e){
			e.printStackTrace();
		}
		System.out.printf("Main: 结束任务\n");
	}
}
日志:


2、基于优先级实现Executor

例子使用优先队列来完成

package com.jack;

import java.util.concurrent.TimeUnit;

public class MyPriorityTask implements Runnable, Comparable<MyPriorityTask>{

	private int priority;
	private String name;
	
	
	public MyPriorityTask(int priority, String name) {
		super();
		this.priority = priority;
		this.name = name;
	}

	
	public int getPriority() {
		return priority;
	}


	public String getName() {
		return name;
	}


	@Override
	public int compareTo(MyPriorityTask o) {
		if(this.getPriority()<o.getPriority()){
			return 1;
		}
		if(this.getPriority() > o.getPriority()){
			return -1;
		}
		return 0;
	}

	@Override
	public void run() {
		System.out.printf("MyPriorityTask: %s 优先级: %d\n", name, priority);
		
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e){
			e.printStackTrace();
		}
	}

}

总结:

1、要用到优先队列必须要实现Runnable、Comparable接口,重写compareTo()和run()方法

package com.jack;

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {
	public static void main(String[] args) throws Exception{
		ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 1, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());
		for (int i=0; i<4; i++){
			MyPriorityTask task = new MyPriorityTask(i, "Task " + i);
			executor.execute(task);
		}
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e){
			e.printStackTrace();
		}
		for (int i=4; i<8; i++){
			MyPriorityTask task = new MyPriorityTask(i, "Task "+i);
			executor.execute(task);
		}
		executor.shutdown();
		try{
			executor.awaitTermination(1, TimeUnit.DAYS);
			
		} catch (InterruptedException e){
			e.printStackTrace();
		}
		System.out.println("Main : 执行完毕了");
	}
}


总结:
  • 1、创建一个ThreadPoolExecutor ,参数列表(2, 2, 1, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>())
  • 第一个参数(2): 表示线程空闲数量为2
  • 第二个参数(2):线程池最大数量
  • 第三个参数(1): 大于空闲的线程等待任务的时间(如果超过时限销毁线程)
  • 第四个参数(TimeUnit.SECONDS): 时间单位
  • 第五个参数:队列对象(PriorityBlockingQueue)
  • 2、执行8个任务

日志:

MyPriorityTask: Task 0 优先级: 0
MyPriorityTask: Task 1 优先级: 1
MyPriorityTask: Task 7 优先级: 7
MyPriorityTask: Task 6 优先级: 6
MyPriorityTask: Task 5 优先级: 5
MyPriorityTask: Task 4 优先级: 4
MyPriorityTask: Task 3 优先级: 3
MyPriorityTask: Task 2 优先级: 2
Main : 执行完毕了

刚开始有两个空闲的线程,只要有任务过来就会立即执行。所以会出现0 1 先执行。


3、实现ThreadFactory接口生成自定义线程

package com.jack;

import java.util.Date;

public class MyThread extends Thread{

	private Date creationDate;
	private Date startDate;
	private Date finishDate;
	
	public MyThread(Runnable target, String name) {
		super(target, name);
		setCreationDate();
		
	}

	

	public void setCreationDate() {
		this.creationDate = new Date();
	}

	public void setStartDate() {
		this.startDate = new Date();
	}



	public void setFinishDate( ) {
		this.finishDate = new Date();
	}
	
	public long getExecutionTime(){
		return finishDate.getTime() - startDate.getTime();
	}

	@Override
	public String toString() {
		StringBuilder buffer = new StringBuilder();
		buffer.append(getName());
		buffer.append(":  ");
		buffer.append("  创建时间:");
		buffer.append(creationDate);
		buffer.append(": 运行时间:");
		buffer.append(getExecutionTime());
		buffer.append(" 毫秒");
		return buffer.toString();
	}

	@Override
	public void run() {
		setStartDate();
		super.run();
		setFinishDate();
	}
	

}

总结:

  • 1、继承Thread线程,使用Thread的构造方法Thread(Runnable target, String name) 
  • 2、打印线程的执行开始时间和持续时间

package com.jack;

import java.util.concurrent.ThreadFactory;

public class MyThreadFactory implements ThreadFactory{
	
	private int counter;
	private String prefix;
	

	public MyThreadFactory(String prefix) {
		super();
		this.counter = 1;
		this.prefix = prefix;
	}


	@Override
	public Thread newThread(Runnable r) {
		MyThread myThread = new MyThread(r, prefix+"-" + counter);
		counter++;
		return myThread;
	}

}

总结: 实现ThreadFactory接口,重写newThread()方法

package com.jack;

import java.util.concurrent.TimeUnit;

public class MyTask implements Runnable {

	@Override
	public void run() {
		try{
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e){
			e.printStackTrace();
		}
		
	}

}

总结:创建一个任务。

package com.jack;

public class Main {
	public static void main(String[] args) throws Exception{
		MyThreadFactory myFactory = new MyThreadFactory("MyThreadFactory");
		MyTask task = new MyTask();
		Thread thread = myFactory.newThread(task);
		thread.start();
		thread.join();
		System.out.printf("Main: 线程信息.\n");
		System.out.printf("%s\n",thread);
		System.out.println("Main:执行结束");
	}
}

日志:

Main: 线程信息.
MyThreadFactory-1:    创建时间:Mon Aug 21 14:32:06 CST 2017: 运行时间:2000 毫秒
Main:执行结束
4、在Executor执行ThreadFactory

Executor框架的机制是允许创建和执行分开执行。它是基于Executor和ExecutorService接口。ThreadPoolExecutor实现这个两个接口,所以它允许你执行两类任务:

  • 实现Runnable接口,任务不会返回结果
  • 实现Callable接口,任务会返回结果。

package com.jack;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Main {
	public static void main(String[] args) throws Exception{
		MyThreadFactory myFactory = new MyThreadFactory("MyThreadFactory");
		ExecutorService executor = Executors.newCachedThreadPool(myFactory);
		MyTask task = new MyTask();
		executor.submit(task);
		executor.shutdown();
		executor.awaitTermination(1, TimeUnit.DAYS);
		System.out.printf("Main: 任务执行结束\n");
	}
}

总结:跟例子3一样类,重写Main方法。采用newCachedThreadPool(ThreadFactory threadFactory);

重写MyThread的run方法,打印日志信息

@Override
	public void run() {
		setStartDate();
		super.run();
		setFinishDate();
		System.out.println(toString());
	}

日志:

Main: 任务执行结束
MyThreadFactory-1:    创建时间:Mon Aug 21 14:51:00 CST 2017: 运行时间:2000 毫秒

5、在计划线程池执行自定义任务

ScheduledThreadPoolExecutor 允许执行两类任务:

  • Delayed tasks(延迟任务) : 任务在一段时间后执行一次
  • Periodic tasks(周期任务) : 任务在一段时间间隔周期执行

延迟任务可以执行Callable和Runnable接口,而周期任务只能执行Runnable接口对象,所有能够在计划线程池执行必须是实现RunnableScheduledFuture接口

package com.jack;

import java.util.Date;
import java.util.concurrent.Delayed;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyScheduledTask<V> extends FutureTask<V> implements 
RunnableScheduledFuture<V>{
	private RunnableScheduledFuture<V> task;
	private ScheduledThreadPoolExecutor executor;
	private long period;
	private long startDate;
	public MyScheduledTask(Runnable runnable, V result, RunnableScheduledFuture<V> task,
			ScheduledThreadPoolExecutor executor) {
		super(runnable, result);
		this.task = task;
		this.executor = executor;
	}

	@Override
	public long getDelay(TimeUnit unit) {
		if(!isPeriodic()) {
			return task.getDelay(unit);
		} else {
			if(startDate==0){
				return task.getDelay(unit);
			} else {
				Date now = new Date();
				long delay = startDate - now.getTime();
				return unit.convert(delay, TimeUnit.MILLISECONDS);
			}
		}
	}

	@Override
	public int compareTo(Delayed o) {
	
		return task.compareTo(o);
	}

	@Override
	public boolean isPeriodic() {
		return task.isPeriodic();
	}

	@Override
	public void run() {
		//如果是周期任务,重新把 此对象放入队列中
		if(isPeriodic() && (!executor.isShutdown())){
			Date now = new Date();
			startDate = now.getTime() +period;
			executor.getQueue().add(this);
		}
		System.out.printf("执行MyScheduledTask之前:%s\n", new Date());
		System.out.printf("执行任务中: 是周期任务? %s\n", isPeriodic());
		super.runAndReset();
		System.out.printf("执行MyScheduledTask之后: %s\n", new Date());
	}

	public void setPeriod(long period) {
		this.period = period;
	}

}

package com.jack;

import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyScheduledThreadPoolExecutor  extends
ScheduledThreadPoolExecutor{

	public MyScheduledThreadPoolExecutor(int corePoolSize) {
		super(corePoolSize);
	}

	/**
	 * 将任务进行包装
	 *  (non-Javadoc)
	 * @see java.util.concurrent.ScheduledThreadPoolExecutor#decorateTask(java.lang.Runnable, java.util.concurrent.RunnableScheduledFuture)
	 */
	@Override
	protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {
		MyScheduledTask<V> myTask = new MyScheduledTask<V>(runnable, null, task, this);
		return myTask;
	}

	@Override
	public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
		ScheduledFuture<?> task = super.scheduleAtFixedRate(command, initialDelay, period, unit);
		MyScheduledTask<?> myTask = (MyScheduledTask<?>) task;
		myTask.setPeriod(TimeUnit.MILLISECONDS.convert(period, unit));
		return task;
	}
	
	
	
}

package com.jack;

import java.util.concurrent.TimeUnit;

public class Task implements Runnable{

	@Override
	public void run() {
		System.out.printf("任务: 开始了\n");
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e){
			e.printStackTrace();
		}
		System.out.printf("任务结束\n");
	}

}

package com.jack;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class Main {
	public static void main(String[] args) throws Exception{
		MyScheduledThreadPoolExecutor executor = new MyScheduledThreadPoolExecutor(2);
		Task task = new Task();
		System.out.printf("Main: %s\n", new Date());
		executor.schedule(task, 1, TimeUnit.SECONDS);
		TimeUnit.SECONDS.sleep(3);
		System.out.printf("Main: %s\n", new Date());
		//执行周期执行3次
		executor.scheduleAtFixedRate(task, 1, 3, TimeUnit.SECONDS);
		TimeUnit.SECONDS.sleep(10);
		executor.shutdown();
		executor.awaitTermination(1, TimeUnit.DAYS);
		System.out.printf("Main: 任务执行结束\n");
	}
}

日志:

Main: Mon Aug 21 15:51:53 CST 2017
执行MyScheduledTask之前:Mon Aug 21 15:51:54 CST 2017
执行任务中: 是周期任务? false
任务: 开始了
Main: Mon Aug 21 15:51:56 CST 2017
任务结束
执行MyScheduledTask之后: Mon Aug 21 15:51:56 CST 2017
执行MyScheduledTask之前:Mon Aug 21 15:51:57 CST 2017
执行任务中: 是周期任务? true
任务: 开始了
任务结束
执行MyScheduledTask之后: Mon Aug 21 15:51:59 CST 2017
执行MyScheduledTask之前:Mon Aug 21 15:52:00 CST 2017
执行任务中: 是周期任务? true
任务: 开始了
任务结束
执行MyScheduledTask之后: Mon Aug 21 15:52:02 CST 2017
执行MyScheduledTask之前:Mon Aug 21 15:52:03 CST 2017
执行任务中: 是周期任务? true
任务: 开始了
任务结束
执行MyScheduledTask之后: Mon Aug 21 15:52:05 CST 2017
Main: 任务执行结束

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值