# 线程

# 实现线程的方法

  • Runnable
  • Callable
  • 继承Thread类

解耦、复用线程提高性能

# 停止线程的方式

interrupt可以通知被停止线程,以实现线程间相互通知、相互协作

while (!Thread.currentThread().isInterrupted() && 有工作要做) {}
1
2
3

原理:

  • 调用某个线程的interrupt()后,这个线程的中断标记位就会被设置成true
  • 休眠中的线程(sleepwait等可以使线程进入阻塞)是可以感受到中断信息的,并且会抛出InterruptedException,同时清除中断信息,将标记位设置为false
  • Thread.currentThread().interrupte()函数可以手动添加中断信息

错误用法:

  • suspend()resume():线程调用后,不会释放锁,就进入休眠,在调用resume()前,不会释放,容易引起死锁问题
  • volitile:生产者消费者模式下,生产者阻塞,在它被叫醒之前无法判断结束标记值。而消费者不消费数据时设置的结束标记有可能永远无法读到
private volatile boolean canceled =false;

try {
  while (!cancled && 有工作要做) {
    工作
  }
} catch (InterruptedException e) {
  e.printStackTrace();
}
1
2
3
4
5
6
7
8
9

正确使用interrupt的方式:

  • 用它请求中断,而不是强制停止
  • 中断的异常处理
    • 声明在方法中,顶层可以感知并捕获到;
    • catch后再次声明中断,以便下次仍可感知到

# 线程状态

  • New 新创建

    • new Thread()没有调用start(),调用后转为Runnable状态
  • Runnalbe 可运行

  • Blocked(Synchroined Lock)被阻塞

    等待其它线程释放monitor锁(等待获取排它锁)

    • 进入synchronized没有获取到锁
  • Waiting(Object.wait,Thread.join,LockSupoport.pack,Object.notify)无限期等待,否则不会分配时间版 等待某个条件,等待其它线程显示唤醒,比如join的线程执行完毕,或者notify() notifyAll()

    • 没有Timeout参数的Object.wait()
    • 没有Timeout参数的Thread.join()
    • LockSupport.park()-------ReentrantLock本质执行了它
  • TImeWaiting:限期等待

    • 设置时间参数的Thread.sleep
    • 设置时间参数的Object.wait
    • 设置时间参数的Thread.join
    • 设置时间参数的LockSupport.packNanos和LockSupport.parkUntil方法
  • Terminated 被终止

  1. 延时等待状态:不会释放任何资源及监视器

  2. 等待阻塞状态:会暂时释放相关线程资源及监视器

# Wait必须在Synchronized同步代码中

  1. 生产者消费者模式下,确保notify方法不会消费者判空和wait之间执行
  2. Wait会释放monitor锁,所以必须在Synchronized中持有这把锁

# 生产者消费者的实现

  1. BlockQueue

  2. Condition

    private Queue queue = new LinkedList();
    private int max = 16;
    private ReentrantLock lock = new ReentrantLock();
    // 没有空
    private Condition notEmpty = lock.newCondition();
    // 没有满
    private Condition notFull = lock.newCondition();
    
    public void put(Object o) {
      lock.lock();
      try {
        while (queue.size() == max) {
          notFull.await();
        }
        queue.add(o);
        notEmpty.signal();
      } finally {
        lock.unlock();
      }
    }
    
    public Object take() {
      lock.lock();
      try {
        while(queue.size() == 0) {
          notEmpty.await();
        }
        Object item = queue.remove();
        notFull.signalAll();
        return item;
      } finally {
        lock.unlock();
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
  3. wait notify

    private int maxSize = 16;
    private LinkedList<Object> storage = new LinkedList<>();
    
    public synchronized void put() {
      while (storage.size() == maxSize) {
        wait();
      }
      storge.add(new Object());
      notifyAll();
    }
    
    public synchronized void take() {
      while(storage.size() == 0) {
        wait();
      }
      Object o = storage.remove();
      notifyAll();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

# 线程安全

  1. 运行结果
    • 多个线程操作同一份数据
  2. 发布和初始化
  3. 活跃性问题
    • 死锁:线程间相互等待资源,又不让出已占有的资源
    • 活锁:程序一直在执行,却拿不到结果
    • 饥饿:程序一直拿不到锁,无法执行

# 线程安全问题场景

  • 访问共享变量或资源
  • 操作依赖时序
  • 不同数据之间需要原子的同时修改
  • 并发环境下使用其它类,但其它类没有声明自己是线程安全的

# 线程池

  • 解决线程生命周期系统开销的问题
  • 统筹内存和CPU的使用,避免资源使用不当
  • 统一管理资源

线程池参数

参数 含义
corePoolSize 核心线程数
maxPoolSize 最大线程数
keepAliveTime 空闲线程存活时间
ThreadFactory 线程工厂,用来创建线程
workQueue 任务队列
Handler 拒绝任务策略
上次更新: : 5 months ago