zoukankan      html  css  js  c++  java
  • JAVA 技术手册 卷1 第十四章『多线程』 读书摘要

    什么是线程

    • 进程受CPU时间片的轮转调度,进而予人多任务并发的感觉。
    • 线程在更低层次上扩展多任务概念,一个进程通常包含多个线程。
    • 进程各自数据独立,而线程共享数据。
    • 数据独立使进程相互通信变得繁难,共享数据又使线程并发暗藏风险。

    创建线程的两种方式:

    • 创建一个任务,受单独线程调度。
      public class Task implements Runnable {
          public void run() {
              // TODO: 具体的执行 
          }
          public static void main(String[] args) {
              Runnable runnable = new Task();
              Thread t = new Thread(runnable);
              t.start();
          }
      }
      
    • 创建一个Thread类的子类,不推荐该方式。
      public class MyThread extends Thread {
          @Override
          public void run() {
              // TODO: 具体的执行任务
          }
      }
      

    中断线程

    • 可以用interrupt方法请求中止线程,线程的中断标志被置位,线程某些情况下应不时检查该标志
      while (!Thread.currentThread().isInterrupted()) {
          // TODO: 接着做
      }
      
    • 如果线程阻塞(调用sleepwait),被interrupt的线程会抛出InterruptedException,不可中断的阻塞不会响应interrupt
    • 如果已被interrupt的线程调用sleep,将会复位中断标志并抛出InterruptedException
    • Thread有两个非常类似的方法,interruptedisInterruptedinterrupted是静态方法,会复位当前线程的中断标志isInterrupted是实例方法,不改变中断标志

    线程状态

    线程一共含有6种状态,记于java.lang.Thread.State

    1. New
    2. Runnable
    3. Blocked
    4. Waiting
    5. Timed waiting
    6. Terminated

    线程不会一直保持运行。
    抢占式调度系统在分配时间片时赋予线程运行权,时间片用完时剥夺线程运行权。
    选择下一线程时会考虑线程的优先级。

    Blocked、Waiting、Timed waiting:

    • 当前线程试图获取对象锁而不得时,进入Blocked状态。当其他线程释放该锁,且当前线程持有该锁进入非Blocked状态。
    • 当线程等待java.util.concurrent库中LockCondition、调用Object.waitThread.join等方法时,进入Waiting状态。此时当前线程在等待另一线程通知线程调度器。
    • 当线程调用Thread.sleepObject.waitThread.joinLock.tryLockCondition.await等待时间参数的超期方法,进入Timed waiting状态。
    • 当一个线程被重新激活时,线程调度器会检查其优先级是否比已在运行的线程优先级高,若是,则挑一个线程剥夺其运行权,选择被激活线程运行。

    线程退出

    • run方法退出而线程退出
    • 因未捕获异常抛出而线程退出

    线程属性

    优先级

    • 每个线程都有优先级。
    • 默认情况下,每个线程继承父线程的优先级。
    • 可以调用Thread.setPriority设置线程的优先级
    • JVM的线程优先级高度依赖于系统,Java线程的优先级被映射到宿主平台的优先级上。
    • Windows上有7个优先级,Java线程优先级会出现多对一的情况。
    • Linux上线程不具有优先级。

    守护线程

    • 线程启动前调用Thread.setDaemon(true)将线程转换为守护线程
    • 当只剩下守护线程时,JVM退出

    Unchecked Exception处理器

    • 调用Thread.setUncaughtExceptionHandler为线程安装异常处理器
    • 调用静态Thread.setDefaultUncaughtExceptionHandler为所有线程安装默认异常处理器
    • 如果不为线程安装异常处理器,默认处理器为该线程的ThreadGroup对象
    • ThreadGroupuncaughtException的处理逻辑如下:
      • ThreadGroup
      • DefaultUncaughtExceptionHandler
      • Throwable不是ThreadDeath实例,线程名字以及Throwable的栈信息输出到System.err
    • 使用Unchecked Exception处理器目的在于健壮线程异常退出的记录,如需恢复当前任务只能新建线程重新执行

    线程同步

    java.util.concurrent.locks.ReentrantLock

    • 示例代码
      private Lock lock = new ReentrantLock();
      
      public void doSomething() {
          lock.lock();
          try {
              // do something
          } finally {
              lock.unlock();
          }
      }
      
    • ReentrantLock 可重入,锁内部有计数机制
    • 构造器ReentrantLock(boolean fair)构建公平策略锁,以牺牲性能的代价偏爱等待时间最长的线程。无法保证线程调度器的绝对公平。

    java.util.concurrent.locks.Condition

    • 一个锁可以含有一个或多个条件对象
    • 调用java.util.concurrent.locks.Lock#newCondition获得一个条件对象
    • 线程在持有锁的情况下调用java.util.concurrent.locks.Condition#await()放弃锁并阻塞在Condition的等待集上
    • 另一线程java.util.concurrent.locks.Condition#signalAll激活因条件对象阻塞的线程,这些线程从等待集中移出,再次接受线程调度器的调度,试图重入锁对象。一旦获得锁对象,将从await调用地方返回并继续执行
    • 示例代码
      while(!(ok to proceed))
          condition.await();
      

    synchronized

    • 每个JAVA对象都有一个内部锁
    • 对象内部锁只有一个Condition
    • 对象内部锁的waitnotifyAll,等价于ConditionawaitsignalAll
    • synchronized方法锁对象
    • synchronized静态方法锁对象的类

    Volatile

    • 不同线程访问同一内存地址可能获得不同的值,因为寄存器或本地缓冲区的存在
    • 编译器可以改变指令执行的顺序而不改变代码的语义
    • 使用锁不必担心数据访问不一致,锁保护的临界区不能重排序指令,必要时刷新本地缓存
    • 将域声明为volatile保证数据访问一致性,但不保证数据修改的原子性

    final

    • final修饰的域保证多线程下数据访问一致性

    Atomic

    • java.util.concurrent.atomic下提供了诸如AtomicIntegerAtomicReference等原子类
    • Atomic既保证数据访问一致性,同时保证了数据修改的原子性

    ThreadLocal

    • 每个线程拿到的变量值都是特属于自己的副本,故没有同步的代价
    • java.util.Random是线程安全的,为提高性能JAVA 7引入了java.util.concurrent.ThreadLocalRandom
    • java.text.SimpleDateFormat是非线程安全的,解决该问题为每一个线程构造一个实例,示例代码如下
      public static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>() {
          @Override
          protected SimpleDateFormat initialValue() {
              return new SimpleDateFormat("yyyy-MM-dd");
          }
      };
      

    Lock's wait and interrupt

    • java.util.concurrent.locks.Lock#lock不能被中断,如果线程在等待获得锁时被中断,被中断线程在获得锁之前一直处于阻塞状态
    • java.util.concurrent.locks.Lock#tryLock(long, java.util.concurrent.TimeUnit)可以被中断,中断将抛出InterruptedException
    • java.util.concurrent.locks.Condition#await(long, java.util.concurrent.TimeUnit)tryLock类似

    write/read Lock

    • java.util.concurrent.locks.ReentrantReadWriteLock读并发,写互斥
    • readLock获取读锁,多个读操作可重入,排斥写锁。
    • writeLock获取写锁,排斥其他所有锁。

    阻塞队列

    队列方法

    • add 添加一个元素,如果队列满则抛错
    • element 返回队列的头元素,如果队列空则抛错
    • offer 添加一个元素返回true,如果队列满则返回false
    • peek 返回队列头元素,如果队列空,返回null
    • poll 移出并返回队列头元素,如果队列空,返回null
    • put 添加一个元素,如果队列满则阻塞
    • remove 移出并返回队列头元素,如果空则抛错
    • take 移出并返回队列头元素,如果空则阻塞

    七种队列

    • ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
    • LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
    • PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
    • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
    • SynchronousQueue:一个不存储元素的阻塞队列。
    • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
    • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

    参考资料

  • 相关阅读:
    nodejs学习笔记
    php操作mysql数据库
    HTML5 新特性总结
    万恶的浏览器兼容问题
    图标字体使用方法
    托管代码
    进程间通信,把字符串指针作为参数通过SendMessage传递给另一个进程,不起作用
    利用自定义消息处理函数的WPARAM或LPARAM参数传递指针
    自定义消息中如果需要定义WPARAM和LPARAM,该怎么使用和分配?
    提高VS2010运行速度的技巧+关闭拼写检查
  • 原文地址:https://www.cnblogs.com/lewh/p/6108029.html
Copyright © 2011-2022 走看看