本章涉及内容:
- 自定义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: 任务执行结束