创建线程的三种方式
- 继承 Thread 类,重写其 run() 方法
从源码可知,Thread 类是 Runnable 接口的实现。
- 属性(少部分属性)
线程名:char name[]
优先级:int priority 最大为10,最小为1,默认为5
是否守护线程:boolean daemon 默认 false - 构造方法(只列出常用的)
构造函数均调用 init 方法,参数不一样
init(Thread group, Runnable target, String name, long stackSize)
Thread()
Thread(Runnable target)
Thread(String name)
Thread(Runnable target,String name) - 方法(常用)
currentThread() :返回当前线程
yield() :让出 cpu ,重回就绪队列等待调度
sleep(long mills) :休眠 mills 毫秒,让出 cpu ,进入阻塞队列,休眠结束回到就绪队列等待调度。
join() :在别的线程中被调用,则直到该线程执行完毕调用线程才会执行。
run() :继承 Thread 类时重写该方法,线程执行内容,如果 Runnable 参数不为空,则调用该 Runnable 对象 target 的 run() 方法。target 可为 Runnable 的实现类实例,也可以是 Thread 的子类实例,因为 Thread 类就是 Runnable 的实现类。
start() :启动线程方法
isAlive() :判断当前线程是否处于活动状态
getId() :获取线程的唯一标识
- 实现Runnable接口,重写 run() 方法
编写了对应类之后,将该类的实例传给 Thread 类的 target 属性,由 Thread 对象调用 start() 方法来执行。 - 实现Callable接口,重写 v call() 方法
其中 v 是返回值,实现 Callable 接口的线程类可以具有返回值,其中 v 即为该线程返回值,用 Future< v > 来接收 v 类型的返回值,并通过其 get() 方法来取出。
停止线程的方法
- 线程执行完毕,正常停止
- 通过
stop()方法,不推荐使用 - 通过 interrupt() 方法,不过调用只是开启中断标志,并未中断线程
- interrupt() 方法
interrupt() :开启中断标志
interrupted() :判断当前线程是否已经中断(类方法)
isInterrupted() :判断线程是否已经中断(实例方法)
使用方案:
- 判断是否已经中断,是则抛出中断异常:throw new InterruptedException() 要记得 catch 异常
//in the run()
if (this.interrupted()) {
throw new InterruptedException();
}
- 调用 sleep(long) 方法之后立即调用 interrupt() 方法,休眠被中断,线程终止
例:
MyThread 线程类中的 run() 中的代码:
// 线程休眠20秒
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
System.out.println("线程在沉睡中被停止,进入catch!");
e.printStackTrace();
}
启动该线程的 mian() 方法中代码:
// 主线程中休眠 2秒, 然后调用上面线程的 interrupt() 方法
MyThread thread = new Mythread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
System.out.println("end!");
- 判断已经中断时,直接用 return ;语句终止线程
暂停线程
在java多线程中,使用 suspend() 方法暂停,使用 resume() 方法恢复线程的执行。
线程状态转移图
下图截自《java多线程编程核心技术》一书
从图中可知,线程一共有六种状态:
NEW:线程刚创建出来的状态
RUNNABLE:线程启动,即执行了 start() 方法之后,进入运行状态
TERMINATED:线程被销毁之后的状态
TIMED_WAITING:执行了 Thread.sleep(sleepTime) 方法,wait(timeout),join(timeout)
BLOCKED:线程在等待锁的时候的状态
WATING:执行了 wait() ,join() 后线程所处的状态
守护线程
java中线程分为两种,一种是用户线程,另一种是守护线程。
守护线程:
守护线程是一种特殊的线程,它的特性有“陪伴”的含义,当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程。Daemon 的作用就是为其他线程的运行提供便利服务。任何一个守护线程都是整个JVM中所有非守护线程的“保姆”,只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着JVM一同结束工作。
线程池
线程池的出现可以让线程进行复用,而不会因为启动大量线程给系统造成负担;当有任务提交,则从线程池中拿出一个线程去执行任务,执行完又会将线程返还给线程池。
- 线程池原理
有多种线程池,一般是调用 Executors 的类方法去创建线程池,我们从 Executors 源码中可以发现,各类线程池都是基于 ThreadPoolExecutor ,传入参数不同形成了功能特性不同的线程池。
所以我们从 ThreadPooExecutor 源码来分析线程池
ThreadPoolExecutor 的构造方法(其中参数最多的构造方法)如下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
-
重要属性
corePoolSize :核心线程池大小
maximumPoolSize:线程池允许的最大线程数
keepAliveTime:线程数大于核心线程数时,处于空闲的线程的存活时间
timeUnit:时间单位,即 keepAliveTime 的单位
workQueue:线程池的任务缓存队列,就是一个阻塞队列,当线程数(应该说是提交上来的任务数)大于核心线程数时,新提交的线程(任务)就放到阻塞队列中等待
threadFactory:线程工厂,用来创建线程
handler:当提交线程(任务)数大于最大线程数时的丢弃策略 -
任务缓存队列
用于缓存等待执行的线程(任务),从 ThreadPoolExecutor 源码可知,其类型是 BlockingQueue< Runnable >,通常有三种类型:
ArrayBlockingQueue:有限任务缓存队列,基于数组实现的先进先出队列,创建时需要指定大小
LinkedBlockingQueue :无限任务缓存队列,基于链表实现的先进先出队列,创建时无需指定大小
SynchronousQueue:无缓冲等待队列,直接提交,并不缓存,提交的任务在队列中,如果未被取出,则后面的任务都无法进入队列。
PriorityBlockingQuene:有优先级的无界阻塞队列。 -
丢弃策略
从 ThreadPoolExecutor 源码可知,该类有四个内部静态类,均为 RejectedExecutionHandler 接口的实现类,四种丢弃策略。
CallerRunsPolicy 类:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
除非线程池关闭,否则都会处理任务。
AbortPolicy类:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
}
丢弃该任务,并抛出异常。
DiscardPolicy类:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
直接丢弃,不抛异常。
DiscardOldestPolicy类:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
如果线程池关闭,直接丢弃该任务;
选择队列中最先进入队列等待的任务将其丢弃,并处理该任务。
2. 线程池工作原理
ThreadPoolExecutor 的 executor 方法的源码如下:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
下面是工作流程:
1、线程池刚被创建出来的时候,线程数为0,此时如果提交任务,则新建线程去执行。
2、当目前线程池中的线程数小于核心线程数时,提交任务就新建线程去执行,添加核心线程成功则返回,不成功执行放入阻塞队列等待被执行。
3、当阻塞队列也满了的时候,如果线程数小于最大线程数,则创建新的线程去执行任务。
4、如果线程数大于最大线程数时则将拒绝执行该任务,实行丢弃策略。
3. 各类线程池的工作原理及特性
FixedThreadPool 固定大小线程池,创建出来的线程和线程池同存活
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory,threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);
}
从源码中可知,FixedThreadPool 的核心线程数和最大线程数相等,空闲线程存活时间为0,因为线程数达到核心线程数时就执行丢弃策略了。阻塞队列使用的是 LinkedBlockingQueue ,无限队列。
CachedThreadPool 无限大小线程池,所有线程存活时间为 60s
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory);
}
核心线程数为0,所有线程创建出来,空闲时存活时间为60s,阻塞队列为 SynchronousQueue
SingleThreadPool 单线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()))
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory));
}
核心线程数和最大线程数均为1,线程存活时间为0,如果无任务提交则线程池也关闭,当线程在忙时,新提交的任务就进入到 LinkedBlockingQueue 阻塞队列等待。