一.多线程:
1.1 线程的实现方式细节:
Thread类本质是实现了Runnable接口的一个实例,代表一个线程的实例。
启动方式是通过Thread类的start()方法,start()方法是一个native方法,启动一个心线程,并执行run方法
1.2 线程的生命周期:
生命周期为:新建,就绪,运行,阻塞,死亡
新建:当使用了new关键字创建了一个线程后。(此时JVM为其分配内存,并初始化其成员变量)
就绪:当线程对象调用了start()方法后。Java虚拟机会为其创建方法调用栈和程序计数器
运行:处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体。
阻塞:因为某种原因放弃了CPU的使用权。
三种情况:等待阻塞,同步阻塞,其他阻塞
等待阻塞:运行的线程执行wait方法
同步阻塞:运行的线程在获得对象的同步锁时,若该同步锁被别的线程占用,则JVM会将其放入线程池
其他阻塞:执行sleep()或者join()方法,或者发出了I/O请求时
死亡:
三种情况----正常结束,异常结束,调用stop
正常结束:run()或者call()方法执行完成,线程正常结束。
异常结束:线程抛出一个未捕获的Exception 或Error
调用stop: 调用该线程的stop方法,容易导致死锁
1.3 线程关键字Interrupt使用细节
使用interrupt()方法中短线程有两种情况:
1.3.1线程处于阻塞状态
当使用了sleep,同步锁的wait,socket中的receiver,accept等方法时,会时线程处于阻塞状态。
当调用线程的interrupt()方法时,会抛出InterruptException异常。
一定要捕获InterruptedException异常之后通过break跳出循环,才能正常结束run方法
1.3.2线程处于非阻塞状态
使用isInterrupted()判断线程的中断标志来推出循环。
1.3.3相关实现代码:
public class ThreadSafe extends Thread {
public void run() {
while (!isInterrupted()){ //非阻塞过程中通过判断中断标志来退出
try{
Thread.sleep(5*1000);//阻塞过程捕获中断异常来退出
catch(InterruptedException e){
e.printStackTrace();
break;//捕获到异常之后,执行 break 跳出循环
}
}
}
}
1.4 线程关键字比较:
1.4.1 sleep和wait的区别:
1.sleep()方法属于Thread类,wait()属于Object类
2.sleep()不释放对象锁,让出cpu给其他线程;
3.调用wait方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,需要调用notify()方法
1.4.2 start()和run()的区别
start()真正实现多线程的运行,此时线程处于就绪(可运行)状态,并没有运行,一旦得到时间片,里面的线程体run()方法就开始执行,其中包含了要执行的这个线程的内容
run()方法只是类的一个普通方法而已,直接调用run,程序依然只有主线程这一个线程,其程序执行路径还是只有一条,还要顺序执行,还要等待run方法体执行完毕后才可以继续执行下面的代码。
调用start方法实现多线程,而调用run方法没有实现多线程 (run没有另起线程,而start才是真正意义的新开线程)
1.5线程池的创建
线程池的顶级接口是Executor,但是其只是一个执行线程的工具,真正的线程池接口时ExecutorServer。
实现线程池的四种方式:
1.5.1 newCachedThreadPool
介绍:可缓存线程池,如果线程长度超过处理需要,可灵活回收空闲线程,若无可回收,则创建新的线程
创建代码:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(index);
}
});
}
1.5.2 newFixedThreadPool
介绍:定长线程池,可控制线程最大并发数,超过的线程会在队列中等待
代码:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
1.5.3 newScheduledThreadPool
介绍:定长线程池,支持定时及周期性任务执行。(安全,强大)
代码:
第一种(定时执行):延迟3秒执行。
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
第二种(定期执行):延迟1秒后每3秒执行一次。
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
1.5.4 newSingleThreadExecutor
介绍:单线程的线程池,之用唯一的工作线程来执行任务,保证按照FIFO,LIFO,优先级执行
代码:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
1.6 线程池的七个参数:
1.6.1 corePoolSize:线程池核心线程大小
介绍:最小的线程数量,即使这些线程处于空闲状态,也不会销毁。除非设置AllowCoreThreadTimeOut,这里的最小线程数量即是corePoolSize
1.6.2 maximumPoolSize:线程池最大线程数量
介绍:一个任务被提交,首先会找空闲存活线程,如果有则直接奖任务交给这个空闲线程来执行,如果没有会缓存到队列中。如果队列满了,才会创建一个新的线程。线程池不会无限制的创建线程,会有一个最大线程数量maximumPoolSize
1.6.3 KeepAliveTime:空闲线程存活时间
介绍:一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后会被销毁
1.6.4 unit:空闲线程存活时间单位
介绍:略
1.6.5 workQueue:工作队列
介绍:新任务提交,会先进入任务队列,jdk提供了四种工作队列
①ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序。新任务进来。放到队尾
②LinkedBlockingQuene:基于链表的无界阻塞队列(其实最大容量为Integer.MAX),按照FIFO排序。不会创建新的线程,参数maxPoolSize不起作用
③SynchronousQuene:一个不缓存任务的阻塞队列,生产者放入一个任务,必须等消费者取出这个任务。
③SynchronousQuene:具有优先级的阻塞队列,优先级通过comparator实现
1.6.6 threadFactory线程工厂
介绍:创建一个新线程时使用的工厂,可以用来设定线程名,是否为daemon线程等等
1.6.7 handler拒绝策略
介绍:当工作队列的任务已经到达最大限制,并且线程池中的线程数量也达到了最大限制,就会采用拒绝策略
1.7 拒绝策略细节
策略1:ThreadPoolExecutor.AbortPolicy()
介绍:抛出java.util.concurrent.RejectedExecutionException异常
策略2:ThreadPoolExecutor.CallerRunsPolicy
介绍:用于被拒绝任务的处理程序,直接在execute方法中调用线程中运行被拒绝的任务。如果程序已关闭,则丢弃
策略3:ThreadPoolExecutor.DiscardPolicy
介绍:用于被拒绝的应用程序,默认情况下他将丢弃被拒绝的任务
策略4:ThreadPoolExecutor.DiscardOldestPolicy()
介绍:先进先出原则,丢弃最老的一个请求,并尝试再次提交任务