目录
Executor
可以继承这个接口,通过实现execute这个方法,来开启线程完成任务。这个继承了executor的类就相当于任务管理器,可以调用这个类的execute方法来将任务交给这个类。
/**
* 认识Executor
* JDK8在线文档地址:https://docs.oracle.com/javase/8/docs/api/index.html
*/
public class Demo1_Executor implements Executor{
public static void main(String[] args) {
Demo1_Executor executor = new Demo1_Executor();
executor.execute(()->{
try {
System.out.println("线程开始");
TimeUnit.MILLISECONDS.sleep(500);
System.out.println("线程结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
public void execute(Runnable command) {
command.run();
}
}
结果
任务开始
任务结束
关于偏向锁的具体介绍
Executorservice
用来管理Executor接口相关的线程池,可以接收其他线程池创建出来的对象。
FutureTask中的Callable和Future
FutureTask可以创建一个有返回值且可以抛异常的任务
Callable对Runnable进行了扩展,Runnable由于重写了,不能抛出异常,但是Callable可以有返回值,也可以抛出异常
Future则是任务的返回值
public class Demo6_Future {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<Integer> task = new FutureTask<>(()->{//new callable
TimeUnit.MILLISECONDS.sleep(500);
return 1000;
});
new Thread(task).start();
System.out.println(task.get());
ExecutorService service = Executors.newFixedThreadPool(5);
Future<Integer> f = service.submit(()->{
TimeUnit.MILLISECONDS.sleep(500);
return 1;
});
System.out.println(f.get());
System.out.println(f.isDone());
}
}
需要注意的是在调用get方法取返回值的时候主线程的被阻塞的
结果
1000
1
true
executors
executor的工具类,可以创建许多线程池
newFixedThreadPool
可以设置线程池大小,队列式的线程池
Executorservice的shutdown方法会关闭线程池,但线程池的线程还是会执行完,并回收线程
shutdownNow方法会关闭线程并且直接停止线程
/**
* 线程池的概念
*/
public class Demo5_ThreadPool {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 6; i++) {
service.execute(()->{
try {
TimeUnit.MICROSECONDS.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
System.out.println(service);
service.shutdown();
System.out.println(service.isTerminated());
System.out.println(service.isShutdown());
System.out.println(service);
TimeUnit.SECONDS.sleep(5);
System.out.println(service.isTerminated());
System.out.println(service.isShutdown());
System.out.println(service);
}
}
结果
java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
false
true
java.util.concurrent.ThreadPoolExecutor@58372a00[Shutting down, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
pool-1-thread-1
pool-1-thread-2
pool-1-thread-4
pool-1-thread-3
pool-1-thread-5
pool-1-thread-1
true
true
java.util.concurrent.ThreadPoolExecutor@58372a00[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 6]
如果把shutdown方法换成shutdownNow,那么就会抛异常,因为直接中断了所有线程
java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
false
true
java.util.concurrent.ThreadPoolExecutor@58372a00[Shutting down, pool size = 5, active threads = 5, queued tasks = 0, completed tasks = 0]
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.luban.demo1.Demo5_ThreadPool.lambda$main$0(Demo5_ThreadPool.java:18)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
pool-1-thread-3
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.luban.demo1.Demo5_ThreadPool.lambda$main$0(Demo5_ThreadPool.java:18)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
pool-1-thread-2
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.luban.demo1.Demo5_ThreadPool.lambda$main$0(Demo5_ThreadPool.java:18)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
java.lang.InterruptedException: sleep interrupted
pool-1-thread-1
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.luban.demo1.Demo5_ThreadPool.lambda$main$0(Demo5_ThreadPool.java:18)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
pool-1-thread-5
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.luban.demo1.Demo5_ThreadPool.lambda$main$0(Demo5_ThreadPool.java:18)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
pool-1-thread-4
true
true
java.util.concurrent.ThreadPoolExecutor@58372a00[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 5]
需要注意的是这种线程池只有关闭了才会回收线程
newCachedThreadPool
缓存式的线程池,来一个任务创建一个线程,空闲的线程就接上,每个线程到了一定时间会回收,每个线程都有各自的存活时间
public class Demo8_CachedPool {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
System.out.println(service);
for (int i = 0; i < 2; i++) {
service.execute(()->{
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
System.out.println(service);
TimeUnit.SECONDS.sleep(60);
System.out.println(service);
TimeUnit.SECONDS.sleep(1);
System.out.println(service);
}
}
这种线程池的线程上限为integer的最大值,而且默认为最大值
结果
java.util.concurrent.ThreadPoolExecutor@7f31245a[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@7f31245a[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
pool-1-thread-2
pool-1-thread-1
java.util.concurrent.ThreadPoolExecutor@7f31245a[Running, pool size = 2, active threads = 0, queued tasks = 0, completed tasks = 2]
java.util.concurrent.ThreadPoolExecutor@7f31245a[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 2]
可以看到因为来了两个任务所以启动了两个线程
newSingleThreadExecutor
每次只有一个任务会执行,一个一个执行
public class Demo9_SingleThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
final int j = i;
service.execute(()->{
System.out.println(j + " " + Thread.currentThread().getName());
});
}
}
}
结果
0 pool-1-thread-1
1 pool-1-thread-1
2 pool-1-thread-1
3 pool-1-thread-1
4 pool-1-thread-1
可以看到任务是一个一个完成的
newScheduledThreadPool
可以设置任务开始执行时间,循环执行的间隔时间
public class Demo10_ScheduledPool {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
service.scheduleAtFixedRate(()->{
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}, 0, 500, TimeUnit.MILLISECONDS);
}
}
结果
pool-1-thread-1
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
............循环执行,不会停止
这个方法的第一个参数是Runnable接口,第二个参数是开始的时间,第二个是循环执行的间隔时间,第四个参数是时间单位
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
newWorkStealingPool
这线程池里面的线程是守护线程,同时这里面的线程如果在执行完自己的任务后会直接从别的线程的任务队列里偷取任务
public class Demo11_WorkStealingPool {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newWorkStealingPool();
System.out.println(Runtime.getRuntime().availableProcessors());
service.execute(new R(1000));
service.execute(new R(2000));
service.execute(new R(2000));
service.execute(new R(2000));
service.execute(new R(2000));
service.execute(new R(2000));
service.execute(new R(2000));
service.execute(new R(2000));
service.execute(new R(2000));
TimeUnit.SECONDS.sleep(10);
}
static class R implements Runnable{
int time;
public R (int t){
this.time = t;
}
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(time + " " + Thread.currentThread().getName());
}
}
}
结果
8
1000 ForkJoinPool-1-worker-1
2000 ForkJoinPool-1-worker-6
2000 ForkJoinPool-1-worker-0
2000 ForkJoinPool-1-worker-2
2000 ForkJoinPool-1-worker-5
2000 ForkJoinPool-1-worker-3
2000 ForkJoinPool-1-worker-4
2000 ForkJoinPool-1-worker-7
2000 ForkJoinPool-1-worker-1
因为里面的线程都是守护线程所以当主线程的睡眠时间更短时会有不一样的现象,现在把时间设为1秒
8
1000 ForkJoinPool-1-worker-1
可以看到只有第一个线程执行完了任务,因为主线程结束了,守护线程也会跟着结束
ForkJoinPool
通过fork和join方法实现任务的切分
public class Demo12_ForkJoinPool {
static int[] nums = new int[1000000];
static final int MAX_NUM = 50000;
static Random r = new Random();
static {
for(int i=0; i<nums.length; i++) {
nums[i] = r.nextInt(100);
}
System.out.println(Arrays.stream(nums).sum());
}
/*static class AddTask extends RecursiveAction {
int start, end;
AddTask(int s, int e) {
start = s;
end = e;
}
@Override
protected void compute() {
if(end-start <= MAX_NUM) {
long sum = 0L;
for(int i=start; i<end; i++){
sum += nums[i];
}
System.out.println(sum);
} else {
int middle = start + (end-start)/2;
AddTask subTask1 = new AddTask(start, middle);
AddTask subTask2 = new AddTask(middle, end);
subTask1.fork();
subTask2.fork();
}
}
}*/
static class AddTask extends RecursiveTask<Long> {
int start, end;
AddTask(int s, int e) {
start = s;
end = e;
}
@Override
protected Long compute() {
if(end-start <= MAX_NUM) {
long sum = 0L;
for(int i=start; i<end; i++){
sum += nums[i];
}
return sum;
}
int middle = start + (end-start)/2;
AddTask subTask1 = new AddTask(start, middle);
AddTask subTask2 = new AddTask(middle, end);
subTask1.fork();
subTask2.fork();
return subTask1.join() + subTask2.join();
}
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
AddTask task = new AddTask(0, nums.length);
forkJoinPool.execute(task);
long result = task.join();
System.out.println(result);
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果
49513285
49513285
想要使用这个线程池必须要有继承RecursiveTask类的对象,同时要把执行的任务重写在compute方法中,在方法中,通过fork来分配子任务,join方法得到子任务的返回值
ThreadPoolExecutor
通过调用ThreadPoolExecutor可以自定义线程池
下面是它的构造器
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
创建具有给定的初始参数和默认线厂和拒绝执行处理程序的一个新的 ThreadPoolExecutor。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
创建具有给定的初始参数和默认线厂一个新的 ThreadPoolExecutor。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
创建一个新的 ThreadPoolExecutor与给定的初始参数和默认拒绝执行处理程序。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
创建具有给定的初始参数的一种新 ThreadPoolExecutor。
我们可以通过返回一个ThreadPoolExecutor,其中的参数可以自定义,这样同样也可以创建一个线程池
这里只对第一个做解释
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
corePoolSize是核心线程多少
maximumPoolSize是最大线程数
keepAliveTime是除核心线程之外的线程最大空闲时间
unit是时间单位
workQueue是每个线程的任务队列类型
execute()
的逻辑应该是:
- 如果当前运行的线程,少于corePoolSize,则创建一个新的线程来执行任务。
- 如果运行的线程等于或多于 corePoolSize,将任务加入 BlockingQueue。
- 如果加入 BlockingQueue 成功,需要二次检查线程池的状态如果线程池没有处于Running,则从 BlockingQueue 移除任务,启动拒绝策略。
- 如果线程池处于 Running状态,则检查工作线程(worker)是否为0。如果为0,则创建新的线程来处理任务。如果启动线程数大于maximumPoolSize,任务将被拒绝策略拒绝。
- 如果加入 BlockingQueue 。失败,则创建新的线程来处理任务。
- 如果启动线程数大于maximumPoolSize,任务将被拒绝策略拒绝。
线程池简要模型
线程池的线程的多少最优一般是cpu的核心数加一