1.继承
Thread
类,重写run()
方法,创建对象后使用start()
方法(不推荐)
2.实现Runnable
接口
3.实现Callable
接口,相比Runnable
多了异步执行的返回值
4.使用lambda
实现,简单常用,但不算一种新方式
public class ThreadMainTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// Thread
new ThreadTest().start();
// Runnable
new Thread(new RunnableTest()).start();
// Callable
FutureTask<String> threadTask= new FutureTask<>(new CallableTest("带返回值的"));
new Thread(threadTask).start();
System.out.println(threadTask.get());
// 4.lambda
new Thread(()->{
System.out.println("lambda:" + Thread.currentThread().getName());
}).start();
}
}
class ThreadTest extends Thread{
@Override
public void run() {
System.out.println("Thread:" + Thread.currentThread().getName());
super.run();
}
}
class RunnableTest implements Runnable {
@Override
public void run() {
System.out.println("Runnable:" + Thread.currentThread().getName());
}
}
class CallableTest implements Callable<String> {
private String name;
public CallableTest(String name) {
this.name = name;
}
@Override
public String call() throws Exception {
System.out.println("Callable:" + Thread.currentThread().getName());
return name;
}
}
使用
ThreadPoolExecutor
构建线程池,不建议使用Executors
创建线程
ThreadPoolExecutor
参见介绍corePoolSize
核心线程数1.
corePoolSize
核心线程数:该线程池内的常驻线程
maximumPoolSize
最大线程:2.
maximumPoolSize
最大线程:
当添加一个任务时,corePoolSize
已满,线程池还没达到maximumPoolSize
,并且没有空闲线程,workQueue
已满的情况下,创建一个新线程并执行。
3.
keepAliveTime
线程回收策略:当池内的线程数大于corePoolSize
并且空闲时长达到keepAliveTime
时,执行回收策略
4.
unit
描述keepAliveTime
的单位
workQueue
工作队列5.
workQueue
工作队列
存放待执行任务的队列:当提交的任务数超过corePoolSize
大小后,再提交的任务就存放在workQueue
,任务调度时再从队列中取出任务。它仅仅用来存放被execute()方法提交的Runnable任务。workQueue
实现了BlockingQueue接口。
JDK默认的工作队列有五种:
ArrayBlockingQueue
数组型阻塞队列:数组结构,初始化时传入大小,有界,FIFO,使用一个重入锁,默认使用非公平锁,入队和出队共用一个锁,互斥。
LinkedBlockingQueue
链表型阻塞队列:链表结构,默认初始化大小为Integer.MAX_VALUE,有界(近似无界),FIFO,使用两个重入锁分别控制元素的入队和出队,用Condition进行线程间的唤醒和等待。
SynchronousQueue
同步队列:容量为0,添加任务必须等待取出任务,这个队列相当于通道,不存储元素。
PriorityBlockingQueue
优先阻塞队列:无界,默认采用元素自然顺序升序排列。
DelayQueue
延时队列:无界,元素有过期时间,过期的元素才能被取出。
threadFactory
线程工厂6.
threadFactory
线程工厂
创建线程的工厂,可以设定线程名、线程编号等。
DefaultThreadFactory
(默认工厂)
rejectedExecutionHandler
拒绝策略7.
rejectedExecutionHandler
拒绝策略
当线程池线程数已满,并且工作队列达到限制,新提交的任务使用拒绝策略处理。可以自定义拒绝策略,拒绝策略需要实现RejectedExecutionHandler接口。
JDK默认的拒绝策略有四种:
AbortPolicy
:丢弃任务并抛出RejectedExecutionException异常。(默认策略)
DiscardPolicy
:丢弃任务,但是不抛出异常。可能导致无法发现系统的异常状态。
DiscardOldestPolicy
:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
CallerRunsPolicy
:由调用线程处理该任务。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS, // unit
new LinkedBlockingQueue<>(), // workQueue
threadFactory, //线程工厂
rejectedExecutionHandler //拒绝策略
);
ScheduledExecutorService
/**
* 带延迟时间的调度,延迟unit单位delay,只执行一次
* 调度之后可通过Future.get()阻塞直至任务执行完毕
*/
1. public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
/**
* 带延迟时间的调度,只执行一次
* 调度之后可通过Future.get()阻塞直至任务执行完毕,并且可以获取执行结果
*/
2. public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
/**
* 带延迟时间的调度,循环执行,固定频率
*/
3. public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
/**
* 带延迟时间的调度,循环执行,固定延迟
*/
4. public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
方法1:schedule(Runnable command, long delay, TimeUnit unit)
@Test
public void test_schedule4Runnable() throws Exception {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture future = service.schedule(() -> {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task finish time: " + format(System.currentTimeMillis()));
}, 1000, TimeUnit.MILLISECONDS);
System.out.println("schedule finish time: " + format(System.currentTimeMillis()));
System.out.println("Runnable future's result is: " + future.get() +
", and time is: " + format(System.currentTimeMillis()));
}
方法2:schedule(Callable
这里代码不再赘述,在
schedule
方法最后加入return
返回即可。和Runnable
基本相同,唯一的区别在于future.get()能拿到Callable返回的真实结果。
@Test
public void schedule4CallableTest() throws Exception {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<String> future = service.schedule(() -> {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task finish time: " + format(System.currentTimeMillis()));
return "success";
}, 1000, TimeUnit.MILLISECONDS);
System.out.println("schedule finish time: " + format(System.currentTimeMillis()));
System.out.println("Callable future's result is: " + future.get() +
", and time is: " + format(System.currentTimeMillis()));
}
方法3:scheduleAtFixedRate(Runnable command,long initialDelay, long period,TimeUnit unit);
固定频率地对一个任务循环执行,任务初始延迟
initialDelay
,任务执行间隔为period
,单位是unit
@Test
public void scheduleAtFixedRateTest() {
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
service.scheduleAtFixedRate(() -> {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task finish time: " + format(System.currentTimeMillis()));
}, 1000L, 1000L, TimeUnit.MILLISECONDS);
System.out.println("schedule finish time: " + format(System.currentTimeMillis()));
while (true) { }
}
方法4:scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
固定延迟地对一个任务循环执行,任务初始延迟
initialDelay
,任务执行间隔为period
。单位是unit
@Test
public void scheduleWithFixedDelayTest() {
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
service.scheduleWithFixedDelay(() -> {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task finish time: " + format(System.currentTimeMillis()));
}, 1000L, 1000L, TimeUnit.MILLISECONDS);
System.out.println("schedule finish time: " + format(System.currentTimeMillis()));
while (true) { }
}
固定频率scheduleAtFixedRate()
和固定延迟scheduleWithFixedDelay()
的区别`
scheduleAtFixedRate()为固定频率,scheduleWithFixedDelay()为固定延迟。固定频率是相对于任务执行的开始时间,而固定延迟是相对于任务执行的结束时间。比如有个任务执行时间特别长,如果使用固定频率,可能会导致当前任务没执行完就开始新的周期任务。这种情况比较适合固定延迟