Java线程
创建线程的几种方式
- 继承Thread,重写run方法;
- Thread结合Runnable;
- Thread结合Callable;
- 使用线程池;
各方式区别:
- 方法1是把线程和任务合并在了一起,方法2、3是把线程和任务分开了
- 用Runnable更容易与线程池等高级 API 配合
- 用Runnable让任务类脱离了Thread继承体系,更灵活
- Callable有返回值,Runnable无返回值
- 线程池节约资源、提高响应速度、提高线程的可管理性、可支持返回值
线程上下文切换时机
- 线程的 cpu 时间片用完
- 垃圾回收
- 有更高优先级的线程需要运行
- 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法
线程常用方法
- start():启动一个线程,操作系统可以调度它了,每个线程只能调用一次,否则抛出异常。
- run():线程执行的任务,直接调用该方法只是普通的方法调用,不是并发。
- join(long n):阻塞在此处,等待调用join的线程执行结束。
- setPriority(int):设置线程优先级,较大的优先级能提高该线程被 CPU 调度的几率
- getState():获取线程状态。
- interrupt():如果此线程在调用Object类的wait()、wait(long)或wait(long, int)方法或join()、join(long)、join(long, int)方法时被阻塞、sleep(long)或sleep(long, int)、此类的方法,则其中断状态将被清除并收到InterruptedException。
如果此线程在InterruptibleChannel的I/O 操作中被阻塞,则通道将关闭,线程的中断状态将被设置,线程将收到java.nio.channels.ClosedByInterruptException 。
如果该线程在java.nio.channels.Selector被阻塞,则该线程的中断状态将被设置,并且它将立即从选择操作中返回,可能具有非零值,就像调用了选择器的wakeup方法一样。
如果前面的条件都不成立,则将设置此线程的中断状态。 - interrupted():判断当前线程是否被中断,会清除打断标记。
- sleep(long n):休眠n毫秒,让出CPU,不释放对象锁
- yield():提示线程调度器让出当前线程对CPU的使用
线程状态
从操作系统层面
初始状态:仅是在语言层面创建了线程对象,还未与操作系统线程关联
就绪状态:准备就绪,等待操作系统调度
运行状态:CPU执行线程代码
阻塞状态:被挂起、IO阻塞、等待锁等
终止状态:线程运行结束
从Java线程层面
Java枚举了以下六个状态
NEW:尚未启动
RUNNABLE:可运行线程的线程状态。正在Java虚拟机中执行,但可能正在等待来自操作系统资源,操作系统层面,不一定在执行
BLOCKED:线程阻塞等待监视器锁的线程状态
WAITING:无时限等待
TIME_WAITING:有时限的等待
TERMINATED:线程运行结束
从上面可以看出,Java中的RUNNABLE涵盖了操作系统层面的可运行、运行和阻塞,Java中的BLOCKED、WAITING、TIME_WAITING是对操作系统层面阻塞状态的细分。
线程池
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:要保留在线程池中的线程数,即使线程处于空闲状态
- maximumPoolSize:允许的最大线程数
- keepAliveTime:当线程数大于核心数时,这是多余空闲线程在终止前等待新任务的最长时间
- unit:keepAliveTime参数的时间单位
- workQueue:用于在执行任务之前保存任务的队列
- threadFactory:执行程序创建新线程时使用的工厂
- handler:达到了线程边界和队列容量,任务无法被线程池处理时的拒绝策略
工作队列
- ArrayBlockingQueue:数组实现,固定容量的队列,若指定为公平队列,则按FIFO顺序处理任务(本质上是指定了公平递归锁),默认非公平。
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
- DelayQueue: 延迟队列提供了在指定时间才能获取队列元素的功能,队列头元素是最接近过期的元素。没有过期元素的话,使用poll()方法会返回null值
- LinkedBlockingQueue:基于链表的无界阻塞队列(若未指定容量,最大容量为Integer.MAX_VALUE)
- PriorityBlockingQueue:基于数组的有界优先级队列
- SynchronousQueue:内部无容器,在某次添加元素后必须等待其他线程取走后才能继续添加(待学习)
- LinkedTrasnferQueue(待学习):源码分析
ThreadFactory
ThreadFactory接口只有一个方法newThread用以产生新线程,若未指定参数,默认为DefaultThreadFactory
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
拒绝策略
AbortPolicy:抛出RejectedExecutionException
DiscardPolicy:忽略此任务
DiscardOldestPolicy:丢弃任务队列中最早的任务,然后重试,除非线程池关闭才放弃当前任务
CallerRunsPolicy:直接在提交任务的线程中执行此任务
线程池原理
/* ctl为AtomicInteger对象,存储了线程池的状态信息 */
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);
/* 避免出现线程池线程数为0,任务不被执行的情况 */
else if (workerCountOf(recheck) == 0) addWorker(null, false);
}
/* 队列满,尝试最大线程数 */
else if (!addWorker(command, false)) reject(command);
乐观锁+双(多)重检查的机制。addWorker 里会再判断ctl,防止启动多余的线程,保证execute方法的线程安全