一、线程的使用
1)重写Thread的run()函数
class MyThread extends Thread {
public int index = 0;
MyThread(int index) {
this.index = index;
}
@Override
public void run() {
System.out.println(index);
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
for (int i = 0; i < 20; i++) {
new MyThread(i).start();
}
}
}
2)实现Runnable接口
class Mytask implements Runnable {
private int index = 0;
public Mytask(int index) {
this.index = index;
}
public void run() {
System.out.println(index);
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
for (int i = 0; i < 20; i++) {
new Thread(new Mytask(i)).start();
}
}
}
3)有返回值的线程
- Callable和FutureTask 获取线程返回值
- futureTask.get()会阻塞调用者线程,直到子线程有反回值。
class CallbleTask implements Callable<Integer> {
public Integer call() throws Exception {
Thread.sleep(1000);
return 100;
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallbleTask callbleTask = new CallbleTask();
FutureTask<Integer> futureTask = new FutureTask<Integer>(callbleTask);
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}
二、线程池
1、ThreadPoolExecutor线程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler){..}
- corePoolSize:核心线程池数量
- 线程池中可以长时间存活的最大线程数量,即这些线程创建后不会被销毁。
- 在线程数少于核心数量时,有新任务进来优先考虑就新建一个线程执行任务。
- 如果新任务进来时,当前所有核心线程都繁忙,则将任务放到工作队列排队(工作队列未满的情况下)。
- 核心线程执行完一个任务后,将检查工作队列是否有等待执行的任务,有任务则取出一个任务执行,反之,则保持空闲状态。
- maximumPoolSize:最大线程数量
- 线程池中包含的最大线程数量,包括核心线程 。
- 如果任务队列满了,并且池中线程数小于最大线程数,会再创建新的线程执行任务。
- 核心线程之外的线程在空闲一段时间后会被销毁,减少占用资源。
- keepAliveTime:核心线程外的线程存活时间,即线程空闲时的存活时间
- workQueue:保存等待执行任务的阻塞队列
当线程池中的核心线程都在执行任务时,再次到来的任务将保存到工作队列中。
- 1)SynchronousQueue(同步队列):该队列不保存任务,作为任务中转站使用,任务进入队列后马上进行提交;如果没有空闲线程,则尝试创建新的线程,如果线程数量达到最大值,则使用拒绝策略;
- 2)ArrayBlockingQueue(有界阻塞队列):如果线程数量小于corePoolSize时,会优先创建新线程,若大于corePoolSize,则会将新任务加入到等待队列;等待队列已满,则在总线程数不大于maxinumPoolSize的前提下,创建新的线程执行任务,若大于,则执行拒绝策略;
- 3)LinkedBlockQueue(无界阻塞队列):除非系统资源耗尽,否则无界的任务队列不存在任务入队列失败的情况;新任务到来时,如果线程数小于corePoolSize, 则创建新的线程执行任务,但当线程池中线程达到poolSize后,就不会增加;
- 4)PriorityBlockingQueue(优先级队列):可以控制任务执行的先后顺序,它是一个特殊的无界队列。
- threadFactory:创建线性的工厂类
- handler:任务拒绝策略;新任务到来,但是线程池已达到最大线程数,且没有线程空闲,有四种策略
-
ThreadPoolExecutor.AbortPolicy:直接抛出 RejectedExecutionException 异常,本策略也是默认的饱和策略。
-
ThreadPoolExecutor.CallerRunsPolicy:使用调用者所在线程来运行任务。
-
ThreadPoolExecutor.DiscardPolicy:忽略新来的任务,什么也不做。
-
ThreadPoolExecutor.DiscardOldestPolicy:把队列中等待时间最久的任务丢弃,然后把新任务保存到队列中。
2、Executors工厂类创建线程池(四种常用线程池)
1)newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 底层是ThreadPoolExecutor。
- 核心线程数和最大线程数相等。
- 不存在非核心线程,所以存活时间和时间单元不起作用。
- 使用无界阻塞队列LinkedBlockingQueue保存待执行任务,容量默认容量 Integer.MAX_VALUE。
2)newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 底层是ThreadPoolExecutor,但是有封装了一层,线程池一经创建就不能够更改 (比如向上转型) 。
- 核心线程和最大线程数相等,且等于1,所有任务串行执行。
- 无界阻塞队列。
3)newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 核心线程为0,最大线程为Integer.MAX_VALUE。
- 所有线程的空闲存活时间为60s。
- 使用同步队列传递任务,该队列不保存任务。
- 新任务到来,直接放到SynchronousQueue中,有空闲线程则直接取出任务执行,没有空闲线程则新建线程执行任务(SynchronousQueue作为新任务过渡的场所)。
4)newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
- 底层是ScheduledThreadPoolExecutor,继承ThreadPoolExecutor类,并实现
ScheduledExecutorService接口。 - 核心线程数量由corePoolSize决定,最大线程数为Integer.MAX_VALUE。
- 核心线程之外的线程存活时间为0,线程执行任务结束就被销毁。
3、线程池使用
1)ThreadPoolExecutor
public static void main(String[] args) {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 60, 10000, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(15));
for (int i = 0; i < 50; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
指定阻塞队列大小,执行结果
注:
- 核心线程为3,最大线程数为60,阻塞队列大小为15。
- 阻塞队列满了之后,才会创建非核心线程。
- thread_1、thread_2和thread_3是核心线程。
- 下面不指定阻塞队列大小,默认队列不会满,所以只使用核心线程执行任务:
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 60, 10000, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
2)newFixedThreadPool
- 含两个固定线程的线程池
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 20; i++) {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
3)newSingleThreadExecutor
- 但线程的线程池,串行执行任务
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 20; i++) {
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
4)newCachedThreadPool
- 任务不会被阻塞到等待队列中,所有任务均会快速获取线程执行。
- 有空闲线程,则直接从队列取出任务执行。
- 没有空闲线程,则立即创建线程
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 50; i++) {
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
4) newScheduledThreadPool
- 提交任务后,延迟5s后执行。
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
for (int i = 0; i < 50; i++) {
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
},10,TimeUnit.SECONDS);
}
}
- 延时3s后开始执行任务,然后每个10s定时执行
public static void main(String[] args) {
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
System.out.println("提交任务: " + df.format(new Date()));
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务: " + df.format(new Date()));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 3, 10, TimeUnit.SECONDS);
}
- 延时3s后开始执行任务;然后,上一个任务执行完成后,延迟10s执行下一次定时任务
public static void main(String[] args) {
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
System.out.println("提交任务: " + df.format(new Date()));
scheduledThreadPool.scheduleWithFixedDelay (new Runnable() {
@Override
public void run() {
System.out.println("执行任务: " + df.format(new Date()));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 3, 10, TimeUnit.SECONDS);
}
注:两个定时任务,scheduleWithFixedDelay 以上一次任务执行结束后开始计时(相邻任务执行时间间隔=上一次任务执行时间+给定的时间间隔);
scheduleAtFixedRate以上一次任务开始执行时计时(相邻任务执行时间间隔=给定的时间间隔)。