Java多线程(一)

2022-09-21 09:50:05 浏览数 (1)

本文最后更新于 493 天前,其中的信息可能已经有所发展或是发生改变。

一、线程

1. 线程方法

  1. new T1().run() 调用run方法,同步的
  2. Thread.yield() 让一下CPU,线程进入等待队列,从RUNNING变为RUNABLE状态
  3. t.join() 等待t线程运行结束再运行

2. 线程状态

注意:Wating的原因

二、关键字

1. volatile

  1. 保证线程可见性
  • MESI
  • 缓存一致性协议
  1. 禁止指令重排序
  • loadfence原语指令(读屏障)
  • storefence原语指令(写屏障)
  • DCL单例(Double Check Lock)
  • DCL指令重排序可能造成的问题
    • new对象的过程
      1. 分配内存(数据为默认值)
      2. 初始化数据
      3. 引用变量指向对象的内存地址
    • 指令重排序后,上诉步骤可能变成1-3-2,执行完3时,假如另一个线程来获取实例,通过Double Check的第一个Check检查时,发现引用变量不为NULL(此时引用变量指向的是刚分配内存但未初始化数据的对象),便拿去使用了,造成事故
  • 单例模式,饥汉式的缺点,程序一启动就创建对象,占内存。饿汉式是需要时再创建对象。

三、锁

1. synchronized

  1. Hotspot:synchronized通过对象头里的两位(总共64位)来标记的
  2. Synchronized加到非静态方法上等效于,synchronized(this){}
  3. Synchronized加到静态方法上等效于,synchronized(T.class){}
  4. synchronized异常会释放锁,可能造成数据异常(其他线程拿到的数据可能是没处理完的)
  5. synchronized的底层实现 jdk早期:重量级-os 后来改进:锁升级(只升不降) 没错,我就是厕所所长!
  • 坑位偏向锁一把,先到先得,本所小线程负责贴标签(记录线程id)
  • 人少请自旋,其他线程为RUNABLE状态,占cpu,但不访问操作系统,在用户态解决,不经过内核态
  • 打转儿的次数够多(JDK1.6规定为10次),或者打转儿的人够多(JDK目前规定为自旋线程超过CPU内核数的一半),我们小线程们负责找重量级老大申请重量级锁-os,其他线程为WATTING状态,不占cpu,适合临界区执行时间长的场景
  1. synchronized(Object)不能锁String常量、Integer、Long,因为可能在不同的地方对同一个对象上了锁
  2. 不阻止指令重排序
  3. 锁的颗粒度尽量小
  4. 锁对象时,尽量使用final来定义引用变量,避免使用过程中,引用变量指向其他对象,造成线程不同,同一个锁,锁的对象不同,引发事故

2. CAS

Compare And Set,无锁优化,自旋

CPU原语支持,运行期间不被打断

代码语言:javascript复制
cas(V,Expected,NewValue)
if V==E
    V=New
otherwise 
    try again or fail

ABA问题

线程1读取的共享变量为A,进行CAS操作,期间其他线程将该变量修改为B后,又修改为A。CAS认为此变量符合预期。

解决方式:加版本号

java.util.concurrent.atomic包下都是CAS原理

atomicLong在并发量较低的环境下,线程冲突的概率比较小,自旋的次数不会很多。但是,高并发环境下,N个线程同时进行自旋操作,会出现大量失败并不断自旋的情况,此时AtomicLong的自旋会成为瓶颈。

AtomicLong中有个内部变量value保存着实际的long值,所有的操作都是针对该变量进行。也就是说,高并发环境下,value变量其实是一个热点,也就是N个线程竞争一个热点。

LongAdder的基本思路就是分散热点,将value值分散到一个数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。

这种做法有没有似曾相识的感觉?没错,ConcurrentHashMap中的“分段锁”其实就是类似的思路。

3. ReentrantLock

需要手动lock和unlock,一般将unlock写到finally里。synchronized遇到异常会自动释放锁,而ReentrantLock不会。

底层为CAS加等待队列

trylock:尝试获取锁

代码语言:javascript复制
boolean locked = false;
try {
   locked=lock.tryLock(time:5,TimeUnit.SECONDS);
System.out.println("m2..." Locked);
} catch(InterruptedException e){
e.printStackTrace();
} finally {
if(locked) lock. unlock();
}

lockInterruptibly允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。

公平锁

参数为true表示为公平锁,请求锁的线程在等待队列里FIFO

代码语言:javascript复制
ReentrantLock lock=new ReentrantLock(true);

4. CountDownLatch

代码语言:javascript复制
CountDownLatch countDownLatch = new CountDownLatch;
countDownLatch.countDown();
try {
     countDownLatch.await();
} catch (InterruptedException e) {
     e.printStackTrace();
}

0 人点赞