一:继承Thread类的方式
#创建一个线程
class MyThred extends Thread{
public MyThred(String name) {
super(name);
}
@Override
public void run() {
// 线程内的操作
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
# 启动线程
public class CtrateThread01 {
public static void main(String[] args) {
new MyThred("thread01").start();
}
}
● 继承Thread类实现多线程方式的问题:
Java只能单继承,所以一个类继承了Thread就不能再继承其它类。
二:实现Runnable接口
1,最繁琐的方式但最容易理解(创建类去实现Runnable接口)
# 创建一个类实现Runnable接口
public class CreateThreadRunable implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) {
CreateThreadRunable runable=new CreateThreadRunable();
// 创建线程
Thread myThread=new Thread(runable);
// 启动线程
myThread.start();
}
2,匿名内部类实现
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}).start();
}
3,Lambda表达式实现(由于Runnable接口是函数式接口)
public static void main(String[] args) {
new Thread(()->{
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}).start();
三 :FutureTask配合Callable接口
由上面的图片可以看出FutureTask类是实现了Runnable接口的。上面那一种方式正式实现Runnable接口并将
对象作为Thread的参数。那么显而易见可以new FutureTask() 作为Thread的参数实现创建线程。
不同的是FutureTask类需要一个参数Callable接口类型的对象(这时就只需要创建一个Callable的对象,
使用匿名内部类,Lambda表达式,或者创建一个类实现Callable接口 其一就能得到Callable类型的对象)
# 创建FutureTask对象
FutureTask<Integer> futureTask=new FutureTask(new Callable() {
@Override
public Integer call() throws Exception {
int result =10;
/** call()方法是有返回值的,返回Object类型,也可以通过FutureTask<Integer>泛型指定
并且返回的值可以通过FutureTask对象的get()取到
*/
return result;
}
});
public static void main(String[] args) {
// 创建线程
Thread myThread=new Thread(futureTask);
// 启动线程
myThread.start();
// 获取返回值
Integer value = futureTask.get();
System.out.println(value);
}
四:使用线程池
Executors类创建线程池的常用方法:
[1] newCachedThreadPool()
● 执行很多短期异步任务,线程池根据需要创建多线程。并在先前创建的线程可用时将重用它们。可扩
容,遇强则强。
[2] newFixedThreadPool(int nThreads)
● 执行长期任务性能好,创建一个线程池,一池有N个固定的线程。
[3] newSingleThreadExecutor()
● 一个任务一个任务的执行,一池一线程。
public static void main(String[] args) {
// 创建线程池(固定线程数量的方式)-参数为创建线程的个数
ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 启动一个线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName() + " " + new Date());
});
}
五:意外情况
图片来自阿里巴巴开发规范,很明显上面的方式前三种显式创建线程和JDK自带java.util.concurrent.Executors
创建线程池的方式都被pass。
六,ThreadPoolExecutor方式(阿里推荐)
1,ThreadPoolExecutor的实例化七个参数:
java.util.concurrent.ThreadPoolExecutor类的构造器(其一):
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
{
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造器七个参数:
[1] corePoolSize:线程池中的常驻核心线程数
[2] maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于等于1
[3] keepAliveTime:多余的空闲线程的存活时间。当前池中线程数量超过corePoolSize时,当空闲时间
达到keepAliveTime的线程会被销毁,直到剩余线程数量等于 corePoolSize
[4] unit:keepAliveTime的时间单位
[5] workQueue:任务队列,被提交但尚未被执行的任务
[6] threadFactory:表示生成线程池中工作线程的工厂, 用于创建线程,一般默认的即可
[7] handler:拒绝策略处理器。当任务队列已满,工作线程也达到了maximumPoolSize,新增的工作
任务将按照某个既定的拒绝策略被拒绝执行。
1.1 任务队列的类型BlockingQueue接口的七个实现类,创建线程时根据业务选择
[1] ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
[2] LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队
列。
[3] PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
[4] DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
[5] SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
[6] LinkedTransferQueue:由链表组成的无界阻塞队列。
[7] LinkedBlockingDeque:由链表组成的双向阻塞队列。
1.2拒绝策略处理器,创建线程时根据业务选择
一般我们创建线程池时,为防止资源被耗尽,任务队列都会选择创建有界任务队列,但这种模式下如果
出现任务队列已满且线程池创建的线程数达到你设置的最大线程数时,这时就需要你指定
ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略,来处理线程池“超载”的情
况。
ThreadPoolExecutor自带的拒绝策略如下:
[1] AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行
[2] CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是
将某些任务回退到调用者,从而降低新任务的流量。
[3] DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前
任务。
[4] DiscardPolicy:该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢
失,这是最好的一种策略。
以上内置的策略均实现了RejectedExecutionHandler接口,也可以自己扩展RejectedExecutionHandler
接口,定义自己的拒绝策略。
2,手写线程池
ThreadPoolExecutor方式自定义线程池同样是通过指定7个必要参数,创建ThreadPoolExecutor对象:
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
// 准备创建线程池所需要的7个参数
int corePoolSize = 3;
int maximumPoolSize = 5;
int keepLiveTime = 10;
TimeUnit timeUnit = TimeUnit.SECONDS;
ThreadFactory threadFactory = Executors.defaultThreadFactory();
ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);
RejectedExecutionHandler policy =
// 当触发拒绝策略所满足条件时会抛出异常:java.util.concurrent.RejectedExecutionException
// new ThreadPoolExecutor.AbortPolicy();
// 当触发拒绝策略所满足条件时会将任务转回调用者(main线程)执行
// new ThreadPoolExecutor.CallerRunsPolicy();
// 当触发拒绝策略所满足条件时会放弃等待时间最长的那个任务
// new ThreadPoolExecutor.DiscardOldestPolicy();
// 当触发拒绝策略所满足条件时会静默丢弃新的任务
new ThreadPoolExecutor.DiscardOldestPolicy();
// 创建线程池对象
ThreadPoolExecutor myThreadPool = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepLiveTime,
timeUnit,
queue,
threadFactory,
policy);
// 就可以使用我们自己的线程池,创建线程了
myThreadPool.execute(() -> {
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在工作");
}
});
}
}