java.util.concurrent

线程生命周期

JUC☆☆☆ - 图1

死锁条件和破除

互请不循

  • 死锁条件与避免:互斥(×)
  • 请求与保持(一次性申请)
  • 不可剥夺(主动释放)
  • 循环等待(按序申请)

    sleep & wait

  • sleep带锁睡觉【暂停执行-自动苏醒】

  • wait释放锁【线程交互/通讯-无参不主动醒】

    Java创建线程的几种方式?

  • 无返回值(Thread、Runnable)

  • 返回值(Callable(Executors可以转化)、FutureTask;execute()、submit()(返回Future))

    继承Thread

    ```java public class MyThread extends Thread { public void run() {
    1. System.out.println("MyThread.run()");
    } }

MyThread t = new MyThread(); t.start();

<a name="e30dcf20"></a>
### 实现runnable接口
```java
public class RunnableTest implements Runnable {
    @Override
    public void run() {
            System.out.println("RunnableTest.run()");
    }
}
Thread t = new Thread(new RunnableTest());
t.start();

Callable/Future带返回值

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) {
        CallableTest callableTest = new CallableTest() ;
        // 因为Callable接口是函数式接口,可以使用Lambda表达式
        FutureTask<String> task = new FutureTask<Integer>((Callable<String>)()->{
          System.out.println("FutureTask and Callable");
          return "hello word";
        });
       try{
           System.out.println("子线程返回值 : " + task.get());
        } catch (Exception e){
           e.printStackTrace();
        }
    }
}

线程池创建

线程终止方式?

  • 正常结束
  • 使用退出标志volatile boolean退出线程
  • Interrupt()结束线程

    • 阻塞后,调用thread.interrupt(); 中断,run中try-catch捕获InterruptedException)
    • 未阻塞
      public class ThreadSafe extends Thread {
      public void run() {
         while (!isInterrupted()) { // 非阻塞过程中通过判断中断标志来退出
             try {
                 Thread.sleep(5*1000);// 阻塞过程捕获中断异常来退出
             } catch (InterruptedException e) {
                 e.printStackTrace();
                 break;// 捕获到异常之后,执行 break 跳出循环
             }
         }
      }
      }
      
  • t.stop()终止线程(采用 stop 是不安全的,Thread Death error/突然释放锁不可控,数据不一致)

    缓存一致性问题是什么?

    针对多核处理器,每个核有自己的高速缓存,共享同一主存。需要缓存一致性协议保证数据一致。指令重排

    锁类型

  • 悲观锁

  • 乐观锁
  • 分布式锁
  • 互斥锁
  • 自旋锁
  • 读写锁
  • 锁粗化:将多个同步块的数量减少,单个同步块的作用范围扩大(比如循环中的锁)
  • 锁消除:共享数据没有竞争,就把锁去掉
  • 适应性自旋锁:自旋时间不固定,虚拟机判断自旋

    并发三性

    原可有
    volatile:可见性、部分有序性
    synchronized/Lock:原子性、可见性、有序性
    atomic:原子性、可见性
特性 volatile synchronized Lock Atomic
原子性 无法保障 可以保障 可以保障 可以保障
可见性 可以保障 可以保障 可以保障 可以保障
有序性 一定程度保障 可以保障 可以保障 无法保障

Volatile

  • 作用:保证变量的内存可见性、禁止指令重排
  • 缓存一致性问题:总线加锁、缓存一致性协议
  • 缓存一致性协议:MESI(写数据时,发现其他CPU有副本则将其他CPU该变量缓存行失效)store buffer
  • 指令重排序(编译器、指令级、内存系统)
  • as-if-serial语义:单线程保证执行结果不改变
  • happens-before语义:保证正确同步的多线程程序的执行结果不被改变
  • 内存屏障:汇编SFENCE/LFENCE/MFENCE,java内存屏障包括StoreStore、StoreLoad、LoadLoad、LoadStore,volatile是StoreStore+写操作+StoreLoad(SSWSL)、读操作+LoadLoad+LoadStore(RLLLS)
  • 性能比synchronized好,但只能用在变量上,不会阻塞,不能保证原子性
  • 缓存一致性协议MESI(缓存行、锁bus、总线风暴)
  • JMM内存模型

JUC☆☆☆ - 图2

  • 可见性:嗅探机制(处理器嗅探总线),强制失效
  • 有序性:禁止指令重排序(lock前缀指令 内存屏障,as-if-serial,happens-before)
  • 跳出死循环

    Synchronized

  • Java关键字/内置锁,解决多线程之间访问资源的同步性

  • 方法(实例加锁)、静态方法(Class加锁)、代码块(Class加锁)
  • 作用内容同一时间只有一个线程在执行
  • <1.6:重量级锁,Monitor依赖操作系统Mutex Lock(用户态和内核态切换)
  • =1.6:自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁
  • 四种状态:无锁、偏向锁、轻量级锁、重量级锁(一般可升不可降)
  • 修饰代码块:monitorenter(对象头计数器为0可以成功获取,获取后+1)、monitorexit(锁计数设为0,锁释放)
  • 修饰方法:flag:ACC_ASYNCHRONIZED
  • 与ReentrantLock相比:都是可重入锁、都可以实现等待通知(notify[All]/wait; Condition)、JVM级别和API级别、RL增加了①公平非公平②等待可中断③可实现选择性通知(锁绑定多个条件)
  • Monitor:EntryList、Owner、WaitSet
  • 修饰:实例对象(对象头)、静态方法(ACC_SYNCHRONIZED)、代码块(monitorenter、monitorexit、count)
  • 锁膨胀:无锁-偏向锁(Mark Word线程信息cas比较)-轻量锁(复制Mark Word为Lock Record,cas尝试改变指针)-自旋-重量锁(用户态内核态切换)
  • 1.6之后新技术:自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁
  • 双重检验锁方式实现单例模式
  • 特性:有序性(as-if-serial、happens-before)、可见性(内存强制刷新)、原子性(单一线程持有)、可重入(计数)、非公平
  • 与Lock的区别(层面、释放、中断、拿锁情况、锁住什么、Lock有读锁、公平)

    CAS

  • 乐观锁

  • 伪代码:update a set value = newValue where value = {执行前查询出来的值}
  • ABA:版本号(值和版本号一起判断,成功则给版本号+1)-> update a set value = newValue, version = version + 1 where value = {执行前查询出来的值} and version = {执行前查询出来的版本号};时间戳

    ThreadLocal

  • 每个线程都有自己专属的本地变量

  • get(), set()

    private static final ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    
  • Thread类有threadLocals和inheritableThreadLocals变量

  • 内部类:ThreadLocalMap存储以ThreadLocal为key的键值对(让线程可以关联多个ThreadLocal变量)
  • key弱、value强->内存泄露,手动调用remove()
  • 解决什么问题:减少临界区范围、线程切换、使用读写锁或copyonwrite机制
  • 源码:ThreadLocalMap(内存泄露)
  • session

    Atomic原子类

  • Integer/Long/Boolean/Reference/IntegerArray/LongArray/ReferenceArray/StampedReference(ABA)/MarkableReference/IntegerFieldUpdater/LongFieldUpdater

  • incrementAndGet() get()
  • CAS+volatile+native(Unsafe.objectFieldOffset())
  • 包含Integer[Array]、Long[Array]、Boolean、Reference[Array]、StampedReference(解决ABA)、MarkableReference等等
  • 线程安全原理:CAS+Volatile+Native(unsafe)

    Lock类

    ReentrantLock

  • NonfairSync(tryAcquire、acquireQueued、CAS)

  • FairSync(hasQueuedPredecessors、可重入)
  • 公平可选、可重入
  • Condition选择性通知

    ReentrantReadWriteLock

  • ReadLock

  • WriteLock

    StampedLock

    AQS(AbstractQueuedSynchronizer)

  • 原理:CLH、state、独占与共享、模板方法

  • 头节点设计
  • 自定义AQS:重写isHeldExclusively、tryAcquire、tryRelease、tryAcquireShared、tryReleaseShared
  • CAS(实际应用、存在的问题(CPU/一个共享变量原子操作/ABA)和解决(-/AtomicReference/时间戳 标志位))
  • CountDownLatch:倒计时器(A等待BCD都完成后开始工作)只有一个构造方法、只会被赋值一次、没有别的方法可以修改count
  • CyclicBarrier:循环栅栏(ABCD都到达后再继续运行)
  • 核心思想:被请求的共享资源(state)空闲,则将当前请求资源线程设置为有效工作线程,并将共享资源设置锁定状态。如果被请求共享资源被占用,则需要一套线程阻塞等待及被唤醒时锁分配的机制,AQS是用CHL队列实现的,即将暂时获取不到所的线程加入队列
  • CHL:虚拟双向队列
  • 模板方法模式:isHeldExclusively()该线程是否正在独占,用到condition才需要实现、tryAcquire(int)独占、tryRelease(int)独占、tryAcquireShared(int)共享、tryReleaseShared(int)共享
  • 共享方式:exclusive独占(公平/非公平)、share共享
  • ReentrantLock:独占(公平/非公平)
  • Semaphore:共享
  • ReentrantReadWriteLock:组合
  • CountDownLatch:共享
  • SynchronousQueue
  • FutureTask

    线程池

  • 降低资源消耗,提高响应速度,提高线程可管理性

  • execute()提交不需要返回值的任务;submit()提交需要返回值的任务(Future)
  • 建议不要用Executors方式创建(Fixed/Single 队列过长OOM、Cache/Schedule 线程过多OOM),ThreadPoolExecutor方式创建
  • corePoolSize核心线程数/maximumPoolSize最大线程数/workQueue队列(ArrayBlockingQueue)/keepAliveTime/unit/threadFactory/handler饱和策略(抛异常、直接丢、自己线程跑、丢最早的)
  • 好处:降低资源消耗、提高响应速度、提高线程可管理性
  • Exercutors弊端:FixedThreadPool 和 SingleThreadExecutor允许请求的队列长度为 Integer.MAX_VALUE ,可能堆积大量的请求,从而导致OOM;CachedThreadPool 和 ScheduledThreadPool允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM
  • ThreadPoolExecutor七大参数:
    int corePoolSize:核心线程数
    int maximumPoolSize:最大线程数
    long keepAliveTime:空闲时间,等待的时间超过了 keepAliveTime才会被回收销毁,allowCoreThreadTimeOut
    TimeUnit unit: keepAliveTime时间单位
    BlockingQueue workQueue:等待队列(LinkedBlockingQueue无界当心OOM、ArrayBlockingQueue有界/加锁/死循环阻塞队列不满时唤醒/入队、Synchronous)
    ThreadFactory threadFactory:创建新线程的时候会用到
    RejectedExecutionHandler handler:饱和策略(AbortPolicy:默认拒绝,直接抛出异常【直接异常】;CallerRunsPolicy:调用当前线程池的所在的线程去执行被拒绝的任务【直接执行】;DiscardPolicy:什么也不干【直接抛弃】;DiscardOldestPolicy:抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去【去旧纳新】;自定义)
  • 使用Has表维护线程的引用
  • submit:使用Future获取任务执行结果
  • 工作流程
    JUC☆☆☆ - 图3
  • 运行状态:Volatile的状态码running/shutdown/stop/terminated
  • largestPoolSize
  • 实际应用(商品详情界面、批处理)
  • 故障:线程池中的线程异常了会被怎么处理?

    • execute方法,可以看异常输出在控制台,而submit在控制台没有直接输出,必须调用Future.get()方法时,可以捕获到异常
    • 一个线程出现异常不会影响线程池里面其他线程的正常执行
    • 线程不是被回收而是线程池把这个线程移除掉,同时创建一个新的线程放到线程池中

      线程通讯方法

  • wait/notify

  • 共享变量sync
  • Lock同步机制
  • volatile
  • CountDownLatch/CyclicBarrier

    进程通讯方法

  • 管道

  • 信号量
  • 信号
  • 消息队列
  • 共享内存
  • Socket

    实现生产者消费者模型

  • 信号量
  • 线程通讯
  • 阻塞队列(双端队列工作密取)

    优雅地手写一个线程安全的单例模式

  • 懒汉式(多线程下数据安全问题)

    public class Singleton {
      private static Singleton singleton;
      private Singleton() {}
      public static Singleton getInstance() {
          if (singleton == null) {
              singleton = new Singleton();
          }
          return singleton;
      }
    }
    
  • 懒汉式加锁(效率低)

    public class Singleton {
      private Singleton singleton;
      private Singleton(){};
      public synchronized Singleton getInstance() {
          if (singleton == null) {
              singleton = new Singleton();
          }
          return singleton;
      }
    }
    
  • 饿汉式(常用,但是有垃圾对象)

    public class Singleton {
      private static Singleton instance = new Singleton();
      private Singleton(){}
      public static Singleton getInstance() {
          return instance;
      }
    }
    
  • 双重校验锁(多线程下性能高)

    public class Singleton {
      private static Singleton singleton;
      private Singleton() {}
      public static Singleton getInstance() {
          if (singleton ==  null) {
              synchronized (Singleton.class) {
                  if (singleton == null) {
                      singleton = new Singleton();
                  }
              }
          }
          return singleton;
      }
    }
    
  • 双重校验锁+volatile

    上面双重校验锁可能会出现如下线程问题:
    在 Singleton 构造函数体执行之前,变量 instance 可能成为非 null 的,即赋值语句在对象实例化之前调用,此时别的线程得到的是一个还会初始化的对象, 也就是 一个线程走到3正在初始化时被挂起,另一个线程走到0,发现instance不为null,就直接返回一个没有初始化完全的对象!
    所以为了避免这种情况发生,我们给instance加上volatile关键字,利用它禁止指令重排序的功能,给写操作添加一个内存屏障。即在完全的初始化完一个对象前,不会调用读操作。

public class Singleton {
    private volatile static Singleton singleton;
    private Singleton() {}
    public static Singleton getInstance() {
        if (singleton ==  null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
  • 静态内部类登记

    和饿汉式同样,使用ClassLoader机制来保证了初始化instance只有一个线程。而和饿汉式不一样的地方在于,使用了Lazy初始化,只有在主动调用getInstance方法时,才会实例化对象。

public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton() {}
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
  • 枚举

    采用单例模式的最佳方法,简洁、自动支持序列化机制,而且避免了线程同步问题。不过目前很少被使用。

public enum Singleton {
    INSTANCE;
    public void whateverMethod() {}
}
  • 使用ThreadLocal实现单例模式(线程安全)

    ThreadLocal为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。

public class Singleton {
    private static final ThreadLocal<Singleton> tlSingleton = new ThreadLocal<Singleton>() {
        @Override
        protected Singleton initialValue() {
            return new Singleton();
        }
    };
    private Singleton(){}
    public static Singleton getInstance() {
        return tlSingleton.get();
    }
}
  • 使用CAS锁实现
    import java.util.concurrent.atomic.AtomicReference;
    public class Singleton {
      private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();
      private Singleton(){}
      public static Singleton getInstance() {
          for (;;) {
              Singleton current = INSTANCE.get();
              if (current != null) {
                  return current;
              }
              current = new Singleton();
              if (INSTANCE.compareAndSet(null , current)) {
                  return current;
              }
          }
      }
    }