JUC笔记(synch、ReentrantLock、volatile等)

58 篇文章 0 订阅
3 篇文章 0 订阅

JMM模型,定义了线程和主内存之间的抽象观念,主内存存储共享变量,每个线程拥有独立的工作内存存储共享变量的副本

多线程下存在问题

  1. 每次对变量的操作,在没有刷新到主内存前,其他线程不可见,会导致在多线程情况下进行变量的++,可能会出现与预期不同的结果。
  2. 重排序
       int a = 0;
        boolean flag = false;
        void fun1(){
            a= 1;
            flag = true;
        }
        void fun2(){
            if (flag) {
            a +=5;
            }
        }

fun1中操作没有依赖关系,不同的指令重排结果可能会导致不同的运算结果。

上边的问题可以使用volatile关键字修饰变量或者加锁synchronizedReentrantLock来实现。

Volatile 内存可见性和禁止指令重排

内存可见性,volatile修饰的变量在使用时会置本地内存变量值无效,读取主内存中最新的值,写操作后会立即刷新到主内存。(写操作将当前处理器中的缓存行数据写回主内存,此操作会使其他cpu缓存行无效)

禁止指令重排,在编译中,读操作和写操作指令前后增加内存屏障,防止重排序把屏障后边的指令排到前边去

内存屏障分为两种:Load Barrier Store Barrier即读屏障和写屏障。

对于Load Barrier来说,在指令前插入Load Barrier,可以让高速缓存中的数据失效,强制从新从主内存加载数据;

对于Store Barrier来说,在指令后插入Store Barrier,能让写入缓存中的最新数据更新写入主内存,让其他线程可见

经典单例模式

双重检查锁

变量需加volatile (为了避免对象new 的指令重排问题, new 分三步,1开辟内存空间,2初始化对象,3将内存地址赋值给引用, 不加volatile 会出现132的情况,多线程获取时,可能存在获取了一个未初始化的对象,从而空指针异常)

Cas 操作

AtomicInteger

私有value变量 volatile修饰

底层更新使用unsafe do while的形式进行cas操作,do 获取主内存的变量值到工作内存,加一操作后,调用native方法进行cas,如果失败循环一直尝试

缺点,1、共享变量冲突频繁时,cas操作一直循环尝试,CPU可能会带来很大开销

2、只能保证一个变量的原子性修改,若是多个变量,则需要引入Synchronized or ReentrantLock

3ABA问题(需要关注过程,版本号解决AtomicStampedReference

Synchronized and ReentrantLock

Syn为java关键字,jvm通过字节码指令来实现

RL则在API层面配合try finally使用,支持超时、公平锁(new时true)、非公平锁,与condition配合支持分组唤醒等待线程,syn只能随机或者唤醒所有等待线程

阻塞队列

BlockingQueue

boolean add(E e);插入,成功返回true,否则报错

boolean offer(E e);插入,成功返回true,否则false

void put(E e) throws InterruptedException;插入,阻塞

boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException; 插入,成功返回true,否则false。超时false退出。

E poll();获取头,删除头,失败返回null

E take() throws InterruptedException;获取头,删除头,阻塞

E poll(long timeout, TimeUnit unit) throws InterruptedException;获取头,删除头,超时返回null

ArrayBlockingQueue

LinkedBlockingQueue

  1. SynchronousQueue 没有缓冲的阻塞队列,若没有消费者take操作,put时会阻塞,反之亦然(CAS实现,take没有发现匹配的put线程,自旋一会睡眠,等待put配对唤醒)。适用一种线程与线程间一对一传递消息,此队列在newCachedThreadPool中被使用

CopyOnWrite

CopyOnWriteArrayList 

在更新操作时,加锁,复制一份原值进行操作,操作后将原值替换。

查询不加锁,读写同步进行,因此读多写少的数据,写入时复制的做法就很不错,存在数据一致性问题,在还没替换之前读取的都是旧数据,也存在内存占用问题,对象较大,在复制时会导致javaGC

线程类Thread

中断interrupt()方法不会真正终止线程,只是将线程中断标志位置true,当线程处于wait,sleep,join方法阻塞中,会退出阻塞状态,抛出InterruptedException,使线程可以继续执行。

推荐终止线程使用如下

Public void run(){

while (!stop) {

// do some

}

}

Public void stop(){

Stop = ture;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值