Java线程

创建线程的几种方式

  1. 继承Thread,重写run方法;
  2. Thread结合Runnable;
  3. Thread结合Callable;
  4. 使用线程池;

各方式区别:

  • 方法1是把线程和任务合并在了一起,方法2、3是把线程和任务分开了
  • 用Runnable更容易与线程池等高级 API 配合
  • 用Runnable让任务类脱离了Thread继承体系,更灵活
  • Callable有返回值,Runnable无返回值
  • 线程池节约资源、提高响应速度、提高线程的可管理性、可支持返回值

线程上下文切换时机

  1. 线程的 cpu 时间片用完
  2. 垃圾回收
  3. 有更高优先级的线程需要运行
  4. 线程自己调用了 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:达到了线程边界和队列容量,任务无法被线程池处理时的拒绝策略

工作队列

blockingQueue
  • 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方法的线程安全

Q.E.D.


一切很好,不缺烦恼。