Thread类定义
-
Thread()一般用于在Thread类中覆盖定义run方法,可以使用匿名内部类进行定义
-
Thread(Runnable)使用最多的情况,run方式是由Runnable参数对象提供
-
Thread(String name)自定义线程名称
-
Thread(Runnable,String name)
-
由于Runnable接口属于函数式接口,所以一般使用简化写法
Thread t = new Thread(()->{ System.out.println(Thread.currentThread()); }); t.start()
Thread常见方法
方法 | 说明 |
---|---|
void start() | 使该线程开始执行,注意不是立即执行,不是一般方法调用;Java 虚拟机调用该线程的 run 方法 |
void run() | 线程的执行体 |
void setName(String) | 改变线程名称 |
void setPriority(int) | 更改线程的优先级,Java中线程的优先级可以分为1-10,默认为5 |
void setDaemon(boolean) | 设置守护线程,守护线程是一种用于提供服务的线程,一般线程体中使用的是死循环,会在所有非守护线程退出后自动关闭 |
void join()/(long millisec) | 等待该线程终止的时间最长为 millis 毫秒 |
void interrupt() | 中断线程,不是中断线程的执行,而是修改中断参数 |
boolean isAlive() | 测试线程是否处于活动状态,活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
static void sleep(long millisec) | 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响 |
static Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
Callable接口
继承Thread或实现Runnable接口这2种方式都有一个缺陷就是:在执行完任务之后无法
获取执行结果。
如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样
使用起来就比较麻烦。
call()方法有返回值,这个返回值可以通过泛型进行约束,允许抛出异常。
Future接口
- Future表示一个任务的生命周期,并提供了方法来判断是否已经完成或取消以及获取任务的结果和取消任务等
- Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
- cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true
- isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true
- isDone方法表示任务是否已经完成,若任务完成,则返回true
有用的方法
-
get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回
-
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就抛出TimeoutException超时异常。
Future接口的具体实现类
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {void run();}
public class Test3 {
public static void main(String[] args) throws InterruptedException {
Future f = new FutureTask(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread() + "...start..." + i);
Thread.sleep(10); //模拟一个处理过程
System.out.println(Thread.currentThread() + "...end..." + i);
}
return null;
});
if (f instanceof Runnable) {
new Thread((Runnable) f).start();
int counter = 0;
while (true) {
Thread.sleep(20);
System.out.println("任务是否被取消:" + f.isCancelled() + "--" + counter);
System.out.println("任务是否执行完毕:" + f.isDone());
counter++;
if (counter > 10)
f.cancel(true);// 取消任务的执行
if (counter > 50)
break;
}
}
}
}
-
FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,
又可以作为Future得到Callable的返回值 -
FutureTask是一个可取消的异步计算,FutureTask实现了Future的基本方法,提供start cancel 操作,可以查询计算是否已经完成,并且可以获取计算的结果。结果只可以在计算完成之后获取,get方法会阻塞当计算没有完成的时候,一旦计算已经完成, 那么计算就不能再次启动或是取消。
-
一个FutureTask 可以用来包装一个 Callable 或是一个Runnable对象。因为FurtureTask实现了Runnable方法,所以一个 FutureTask可以提交(submit)给一个Excutor执行(excution),它同时实现了Callable, 所以也可以作为Future得到Callable的返回值。
线程池
ThreadPoolExecutor是线程池框架的一个核心类,线程池通过线程复用机制,并对线程进行统一管理
- 降低系统资源消耗。通过复用已存在的线程,降低线程创建和销毁造成的消耗;
- 提高响应速度。当有任务到达时,无需等待新线程的创建便能立即执行;
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗大量系统资源,还会降低系统的稳定性,使用线程池可以进行对线程进行统一的分配、调优和监控。
线程池的运行状态总共有5种,其值和含义分别如下:
- RUNNING: 高3位为111,接受新任务并处理阻塞队列中的任务
- SHUTDOWN: 高3位为000,不接受新任务但会处理阻塞队列中的任务
- STOP: 高3位为001,不会接受新任务,也不会处理阻塞队列中的任务,并且中断正在运行的任务
- TIDYING: 高3位为010,所有任务都已终止,工作线程数量为0,线程池将转化到TIDYING状态,即将要执行terminated()结束钩子方法
- TERMINATED: 高3位为011,terminated()方法已经执行结束
ThreadPoolExecutor构造器中各个参数的含义
corePoolSize
线程池中的核心线程数。当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行。
maximumPoolSize
线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize。
keepAliveTime
线程空闲时的存活时间。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,keepAliveTime参数也会起作用,直到线程池中的线程数为0。
unit
keepAliveTime参数的时间单位。例如TimeUnit.DAYS
workQueue
任务缓存队列,用来存放等待执行的任务。如果当前线程数为corePoolSize,继续提交的任务就会被保存到任务缓存队列中,等待被执行。
一般来说,这里的BlockingQueue有以下三种选择:
- SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。因此,如果线程池中始终没有空闲线程(任务提交的平均速度快于被处理的速度),可能出现无限制的线程增长。
- LinkedBlockingQueue:基于链表结构的阻塞队列,如果不设置初始化容量,其容量Integer.MAX_VALUE,即为无界队列。因此,如果线程池中线程数达到了corePoolSize,且始终没有空闲线程(任务提交的平均速度快于被处理的速度),任务缓存队列可能出现无限制的增长。
- ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务。
threadFactory
线程工厂,创建新线程时使用的线程工厂。
handler
任务拒绝策略,当阻塞队列满了,且线程池中的线程数达到maximumPoolSize,如果继续提交任务,就会采取任务拒绝策略处理该任务,线程池提供了4种任务拒绝策略:
- AbortPolicy:丢弃任务并抛出RejectedExecutionException异常,默认策略;
- CallerRunsPolicy:由调用execute方法的线程执行该任务;
- DiscardPolicy:丢弃任务,但是不抛出异常;
- DiscardOldestPolicy:丢弃阻塞队列最前面的任务,然后重新尝试执行任务(重复此过程)。当然也可以根据应用场景实现RejectedExecutionHandler接口自定义饱和策略,如记录日志或持久化存储不能处理的任务。
Executors创建线程池
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收重用时则新建线程
-
new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务
-
newFixedThreadPool
创建一个固定大小的定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
-
new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于可以预测线程数量的业务中,或者服务器负载较重,对当前线程数量进行限制
-
newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景
-
newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO先进先出, LIFO后进先出, 优先级)执行适用于需要保证顺序执行各个任务,并且在任意时间点,不会有多个线程是活动的场景
-
newWorkStealingPool
创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行
public class Test2 {
public static void main(String[] args) {
// ExecutorService es=Executors.newCachedThreadPool();
// ExecutorService es=Executors.newFixedThreadPool(3);
ExecutorService es = Executors.newSingleThreadExecutor();
for (int i = 0; i < 1000; i++) {
int kk = i;
es.submit(() -> {
for (int k = 0; k < 5; k++)
System.out.println(Thread.currentThread() + "::" + kk);
});
}
es.shutdown();
}
}
ExecutorService提供提交任务的方式
- 线程池框架提供了两种方式提交任务,submit()和execute()。
- 通过submit()方法提交的任务可以返回任务执行的结果。
- 通过execute()方法提交的任务不能获取任务执行的结果。
public class Test4 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Future[] fs=new Future[10];
ExecutorService es = Executors.newFixedThreadPool(3);
// es.execute(null); 这里的参数为Runnable,所以不会有返回值
for (int i = 1; i <= 10; i++) {
int begin = (i - 1) * 100 + 1;
int end = i * 100;
fs[i-1]=es.submit(() -> { //参数可以是Callable或者Runnable,如果Runnable则没有返回值,Callable则可以通过Future获取返回值
int res = 0;
for (int k = begin; k <= end; k++) {
res += k;
}
return res;
});
}
int res=0;
for(Future<Integer> f:fs)
res+=f.get();
System.out.println(res);
}
}
ExecutorService的方法:关闭线程池
- shutdownNow:对正在执行的任务全部发出interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表
- shutdown:当调用shutdown后,线程池将不再接受新的任务,但也不会去强制终止已经提交或者正在执行中的任务