前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java多线程(一)

Java多线程(一)

作者头像
Yuyy
发布2022-09-21 09:50:05
1550
发布2022-09-21 09:50:05
举报
文章被收录于专栏:yuyy.info技术专栏

本文最后更新于 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();
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-2-20 1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、线程
    • 1. 线程方法
      • 2. 线程状态
      • 二、关键字
        • 1. volatile
        • 三、锁
          • 1. synchronized
            • 2. CAS
              • 3. ReentrantLock
                • 4. CountDownLatch
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档