线程经常使用类和接口以下:java
1.Thread和Runnable是最经常使用的,区别是一个是类,一个是接口。类的缺点是只能单继承,而接口没有这个限制。编程
2.Thread和Runnable没有返回值,想获得线程执行结果,要么是经过构造器事先注入存储执行结果的对象,要么是经过生产者-消费者模式获得。为了直接获得线程执行结果,java并发包增长了Callable,不过Callable须要经过ExecutorService来提交执行,而不能简单经过Thread注入后执行。缓存
3.Future是线程池中线程执行后的返回结果,另外经过它能够终止线程执行,判断线程的完成状态等等。安全
4.一般状况下,使用以上几个接口和类已经足够,FutureTask做为补充,使得能够不用走左边线程池那条线,就能执行线程并获得线程执行结果,能够理解为是一种轻量级的调用方式,它的调用过程以下:bash
FetureTask task = new FutureTask(new Callable()); //这里的Callable须要自行实现
Thread thread = new Thread(task); //这里能够看到FetureTask实现Runnable接口的目的:能被看成线程任务执行。
V v = task.get(); //这里能够看到FetureTask实现Future接口的目的:获得线程任务执行结果。
复制代码
5.Executors是线程池工厂,生成不一样类型的线程池。Executors可能引发内存溢出,有的大厂不推荐使用,而是推荐经过ThreadPoolExecutor人工建立线程池。并发
6.ExecutorService是线程执行服务,负责执行/提交线程任务。dom
7.ThreadPoolExecutor用于人工建立线程池,同时也能够执行/提交线程任务。函数
以上就是线程经常使用的类和接口,每一个都写个demo练习一下,很快就能掌握,使用时也能了然于胸。post
虽然有的大厂不推荐使用Executors,甚至做为一项代码检查规则给予提示,可是根据实际业务场景须要,若是没有致使内存溢出的场景,仍是能够用的。优化
能够经过Executors.newFixedThreadPool(nThreads:int)方法来建立固定大小的线程池,而后ExecutorService来提交要执行的线程任务,这里因为没有限制ExecutorService能提交多少任务,所以可能致使提交任务太多而致使内存溢出。代码示例:
public class FixedThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " made rice.");
}
});
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " made dish.");
}
});
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " clean up table.");
}
});
executorService.shutdown();
}
}
复制代码
输入日志:
pool-1-thread-1 made rice.
pool-1-thread-2 made dish.
pool-1-thread-2 clean up table.
Process finished with exit code 0
复制代码
日志能够看出,线程池里有两个线程,总共3个任务被执行。
能够经过Executors.newSingleThreadExecutor()来快速建立只有一个线程的线程池,提交到这个线程池里的多个任务只能按照顺序一个个的执行。代码示例:
public class SingleThreadDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " made rice.");
}
});
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " made dish.");
}
});
executorService.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " clean up table.");
}
});
executorService.shutdown();
}
}
复制代码
输出日志:
pool-1-thread-1 made rice.
pool-1-thread-1 made dish.
pool-1-thread-1 clean up table.
Process finished with exit code 0
复制代码
能够只有一个线程执行多个任务。
线程池中的任务能够按照计划执行,代码示例以下:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/** * @ClassName ScheduledThreadPoolDemo * @Description 按计划执行任务线程池demo * @Author 铿然一叶 * @Date 2019/10/11 16:13 * @Version 1.0 * javashizhan.com **/
public class ScheduledThreadPoolDemo {
public static long fixedRateInterval = 0;
public static long lastFixedRateRunTime = System.nanoTime();
public static long withFixedInterval = 0;
public static long lastWithFixedRunTime = System.nanoTime();
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
//1秒后执行,只执行1次
executorService.schedule(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " made rice.");
}
}, 1, TimeUnit.SECONDS);
//1秒后开始执行,每间隔3秒执行一次,这个间隔时间是从前一个任务【执行开始时间】算起
lastFixedRateRunTime = System.nanoTime();
executorService.scheduleAtFixedRate(new Runnable() {
public void run() {
long runTime = System.nanoTime();
fixedRateInterval = runTime - lastFixedRateRunTime;
lastFixedRateRunTime = runTime;
//模拟任务执行
sleep(2); //这个休眠时间不影响下次执行间隔时间的计算,执行间隔是3秒(当任务执行耗时小于3秒时,若是大于3秒了,则间隔为任务的执行耗时)
System.out.println(Thread.currentThread().getName() + " made dish. Interval:" + fixedRateInterval);
}
}, 1, 3,TimeUnit.SECONDS);
//两秒后执行任务,每次任务之间间隔3秒,这个间隔时间是从前一个任务【执行结束时间】算起
lastWithFixedRunTime = System.nanoTime();
executorService.scheduleWithFixedDelay(new Runnable() {
public void run() {
long runTime = System.nanoTime();
withFixedInterval = runTime - lastWithFixedRunTime;
lastWithFixedRunTime = runTime;
//模拟任务执行
sleep(2); //这个休眠时间会影响下次执行间隔时间的计算,执行间隔是2秒加上本次运行时间
System.out.println(Thread.currentThread().getName() + " clean up table. Interval:" + withFixedInterval);
}
}, 2, 3, TimeUnit.SECONDS);
}
public static void sleep(long millis) {
try {
TimeUnit.SECONDS.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
输出日志:
pool-1-thread-1 made rice.
pool-1-thread-2 made dish. Interval:1000771200
pool-1-thread-3 clean up table. Interval:2002448300
pool-1-thread-2 made dish. Interval:3000158800
pool-1-thread-2 made dish. Interval:2999740199
pool-1-thread-1 clean up table. Interval:5001442200
pool-1-thread-2 made dish. Interval:3000925301
pool-1-thread-3 clean up table. Interval:5000781199
pool-1-thread-1 made dish. Interval:2999058400
pool-1-thread-1 made dish. Interval:3000752800
pool-1-thread-2 clean up table. Interval:5001498901
pool-1-thread-1 made dish. Interval:2999442900
pool-1-thread-1 made dish. Interval:3001681600
pool-1-thread-3 clean up table. Interval:5001714800
复制代码
能够看到:
1.made rice执行了一次
2.clean up table首次2秒后执行,后续间隔为5秒(任务间隔时间+任务执行时间)
3.made dish首次在1秒后执行,后续间隔为3秒(任务间隔时间,没有加上任务执行时间,而且任务执行时间小于间隔时间,因此按照间隔时间执行)
经过Executors.newCachedThreadPool()方法能够建立可缓存的线程池,默认池大小为0,最大值为Integer.MAX_VALUE,若是有线程超过60秒未使用就会自动释放。
Executors的每个建立线程池的方法都是经过ThreadPoolExecutor来构造的,建立缓存线程池调用的构造函数以下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
复制代码
下一章节将介绍ThreadPoolExecutor,这里就再也不赘述。
ThreadPoolExecutor手动建立线程池的好处有:
1.能够设置最大线程池大小,支持线程池伸缩。
2.能够使用有界队列来存储待执行的任务,避免任务过多致使内存溢出。
3.能够设置任务拒绝策略,当线程池的任务缓存队列已满而且线程池中的线程数目达到maximumPoolSize时使用。
以下是ThreadPoolExecutor的构造参数定义:
序号 | 名称 | 类型 | 含义 |
---|---|---|---|
1 | corePoolSize | int | 核心线程池大小 |
2 | maximumPoolSize | int | 最大线程池大小 |
3 | keepAliveTime | long | 线程最大空闲时间,时间单位在下一个参数定义 |
4 | unit | TimeUnit | 时间单位 |
5 | workQueue | BlockingQueue | 线程等待队列 |
6 | threadFactory | ThreadFactory | 线程建立工厂 |
7 | handler | RejectedExecutionHandler | 拒绝策略 |
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/** * @ClassName ThreadPoolExecutorDemo * @Description * @Author 铿然一叶 * @Date 2019/10/11 16:54 * @Version 1.0 * javashizhan.com **/
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
//任务队列容量
final int QUEUE_CAPACITY = 20;
BlockingQueue<Runnable> runnableBlockedQueue = new LinkedBlockingDeque<Runnable>(QUEUE_CAPACITY);
//建立线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(4,10,60, TimeUnit.SECONDS, (BlockingQueue<Runnable>) runnableBlockedQueue);
int taskNo = 0;
int queueSize = 0;
while (true) {
//随机生成任务数
int taskNum = getTaskNum();
for (int i = 0; i < taskNum; i++) {
taskNo++;
//队列未满则提交新任务
if (queueSize < QUEUE_CAPACITY) {
executor.execute(new Worker("Task" + taskNo));
} else {
//队列满了则等待一会重试, 注意:若是队列满了还提交任务会由于超出队列容量而报错。
while(true) {
sleep(200);
queueSize = executor.getQueue().size();
if (queueSize < QUEUE_CAPACITY) {
executor.execute(new Worker("Task" + taskNo));
break;
}
}
}
queueSize = executor.getQueue().size();
}
}
}
//随机生成一次执行的任务数
private static int getTaskNum() {
return ((int)(1+Math.random()*(8-1+1)));
}
private static void sleep(long millis) {
try {
TimeUnit.MILLISECONDS.sleep(millis);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
class Worker implements Runnable {
private String name;
public Worker(String name) {
this.name = name;
}
public void run() {
exec();
}
//模拟任务执行
private void exec() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + " be called.");
}
}
复制代码
输出日志:
Task3 be called.
Task4 be called.
Task2 be called.
Task1 be called.
Task6 be called.
Task5 be called.
Task7 be called.
Task8 be called.
Task9 be called.
Task10 be called.
Task12 be called.
Task11 be called.
Task13 be called.
Task14 be called.
Task15 be called.
Task16 be called.
Task17 be called.
Task18 be called.
Task19 be called.
Task20 be called.
Task21 be called.
复制代码
以上就是线程经常使用类和接口了。
end.
相关阅读:
Java并发编程(一)知识地图
Java并发编程(二)原子性
Java并发编程(三)可见性
Java并发编程(四)有序性
Java并发编程(五)建立线程方式概览
Java并发编程入门(六)synchronized用法
Java并发编程入门(七)轻松理解wait和notify以及使用场景
Java并发编程入门(八)线程生命周期
Java并发编程入门(九)死锁和死锁定位
Java并发编程入门(十)锁优化
Java并发编程入门(十一)限流场景和Spring限流器实现
Java并发编程入门(十二)生产者和消费者模式-代码模板
Java并发编程入门(十三)读写锁和缓存模板
Java并发编程入门(十四)CountDownLatch应用场景
Java并发编程入门(十五)CyclicBarrier应用场景
Java并发编程入门(十六)秒懂线程池差异
Java并发编程入门(十八)再论线程安全
Java极客站点: javageektour.com/