1. 我们来温习下多线程的实现方式
1 继承 thread
public class myThread extends Thread{
@override
run{
system.out.println("你好我是线程");
}
}
启动线程
myThread mt = new myThread ();
mt.start();
2 实现接口Runnable
public class myThread implment Runnable{
@override
run{
system.out.println("你好我是线程");
}
}
启动线程
myThread mt = new myThread ();
Thread tt = new Thread (mt);
tt.start();
3 实现callable
public class mycall implement callable<Integer>{
@override
public Integer call{
system.out.println("你好我是线程");
retrun 0;
}
}
启动线程
mycall mc = new mycall();
FutureTask ft = new FutureTask(mc);
Thread thread =new Thread(ft);
thread.start();
system.out.println(ft.get());
4 使用线程池
启动方法
ExecutorService theadPool = Executor.newFixedThreadPool(4);
ArrayList<Future<Status>> arrayList=new ArrayList<Future<Status>>();
for (int i = 1; i <=4; i++) {
mycall mc = new mycall();
Future<Integer> ft = theadPool.submit(mc);
arrayList.add(ft);
}
theadPool.shutdown;
for (Future<Status> future2 : arrayList) {
System.err.println(future2.get().name());
}
2 . 你有没有使用concurrent工具类
Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包
点这里
接口Executor Runnable 是中有run方法
public interface Executor {
void execute(Runnable command);//执行一个Runnable对象
}
ExecutorService 是Executor接口的实现类,其子类有AbstractExecutorService,SchedualedExecutorService
Executors 是一个工具类,提供方法返回ExecutorService来创建线程池
点这里
Executors 所建立的线程池实际使用ExecutorService的子类ThreadPoolExecutor,我们来看个例子
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
所以Executors 只是一个帮我们填好参数的调用ThreadPoolExecutor的工具类,看到一些文章中阿里发布的java守则中建议不使用Executors来建立线程池,所以我们等下再来认真学习下ThreadPoolExecutor内的参数。
Executors 的四种线程池
newCachedThreadPool 创建有缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长的线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
例子:
ExecutorService theadpool = Exectors.newFixThreadPool(5);
Mycall mycall = new Mycall();
Future <Status> future = theadpool.submit(mycall );
线程池的5种状态:
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
- RUNNING 运行
状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。 - SHUTDOWN 停止运行
状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。 - STOP
状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。 - TIDYING
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。 - TERMINATED
线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
关闭线程池:
shutdown() 停止加入新的线程任务
shutdownNow() 中断在执行的线程任务,清空缓存队列
线程池的工作过程如下:
- 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
- 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
1、 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
2 、 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
3 、如果这个时候队列满了,而且正在运行的线程数量小于maximumPoolSize,那么创建线程运行这个任务;
4 、如果队列满了,切正在运行的线程数量大于等于maximumPoolSize,那么就执行拒绝策略。 - 当一个线程完成任务时,它会从队列中取下一个任务来执行。
- 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
3 如何控制线程执行顺序
1 Executors.newSingleThreadExecutor
newSingleThreadExecutor产生一个单线程的线程池,而这个线程池的底层原理就是一个先进先出(FIFO)的队列。按照加入线程池的顺序执行。
ExecutorService theadpool = Exectors.newSingleThreadExecutor;
Mycall mycall = new Mycall();
Future <Status> future = theadpool.submit(mycall );
2 join
使用线程的join方法,该方法的作用是“等待线程执行结束”,即join()方法后面的代码块都要等待现场执行结束后才能执行。 实质是将主线程阻塞,等到线程执行完。
myThread my = new myThread();
Thread thread = new Thread(my);
thread.start();
thread.join;
4 ThreadPoolExecutor
点这里更好的理解线程池
ThreadPoolExecutor是ExecutorService的具体实现类,创建线程池一般使用这个类的方法,
例如:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
我们来解释下其中的参数:
- corePoolSize:线程池的核心线程数,线程池中运行的线程数也永远不会超过 corePoolSize 个,默认情况下可以一直存活。可以通过设置allowCoreThreadTimeOut为True,此时 核心线程数就是0,此时
- keepAliveTime控制所有线程的超时时间。
- maximumPoolSize:线程池允许的最大线程数;
- keepAliveTime: 指的是空闲线程结束的超时时间。当一个线程无事可做,超过keepAliveTime时,线程池会判断如果当前运行的线程数大约corePoolSize,那么这个线程就会被停掉。
- unit :是一个枚举,表示 keepAliveTime 的单位;
- workQueue:表示存放任务的BlockingQueue<Runnable队列。
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
ArrayBlockingQueue
ArrayBlockingQueue是一个用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。
LinkedBlockingQueue
LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出的原则对元素进行排序。
SynchronousQueue
SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。
了解了队列的基本知识,我们再回头看下四种线程池
- newCachedThreadPool
使用队列SynchronousQueue,多大线程数为Integer.MAX_VALUE,超时时间为60L, 时间单位为TimeUnit.SECONDS,
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- newFixedThreadPool
使用队列LinkedBlockingQueue
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- newScheduledThreadPool
使用队列DelayedWorkQueue 实现类是ScheduledThreadPoolExecutor,核心线程大小corePoolSize
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
- newSingleThreadExecutor
使用LinkedBlockingQueue队列实现,最大线程数为1
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
下面只是简单的写一下,具体的事例有时间了再补上
5 线程间通讯及同步
- synchronized
可以锁住资源不被其他线程修改 - wait/notify机制
wai/notify 是object的方法
6 阻塞队列和非阻塞队列
点这里
实现队列的阻塞实质上对队列中的数据lock
而非阻塞队列没有lock
使用阻塞队列与非阻塞队列实现生产者和消费者。
7 线程池的拒绝策略
这个好
有四种拒绝策略
- 1 DiscardPolicy
放弃要加入队列的任务 - 2 DiscardOldestPolicy
放弃在队列最前端的任务 - 3 RejectedExecutionException
抛出异常 - 4 CallerRunsPolicy
直接执行execute
如何停止正在执行的线程
点这里
1 使用stop
2 使用interrupt
3 使用退出标识,使得线程正常退出
那么如何停止线程池中的线程呢
点这里
shutdown
shutdownNow