初识线程Ⅵ之各种锁

回顾

ThreadLocal缺点

  1. 不可继承性(子线程不能读取父线程的变量)
  2. 脏数据(ThreadPool[复用]线程的复用会复用线程相关的静态变量)
  3. 内存溢出[原因:ThreadPool(长生命周期 )–>Thread -->ThredLocal—>ThreadLocalMap --> Entry --> key,value(强引用)]

单例模式

  1. 饿汉方式(线程安全的)
  2. 懒汉方式(线程安全)DCL 双重校验锁

阻塞式队列

生产者在队列满的情况下就会休眠(sleep、wait、LockSupport)
消费者在队列为空的时候进行休眠

乐观锁

它认为一般情况下,不会发生冲突,所以只有在进行数据更新的时候,才会检测并发冲突,如果没有冲突则直接修改,如果有冲突则返回失败

CAS、ABA、JUC

CAS(Compare and swap:比较且交换
V(内存值)
A(预期旧值)
B(新值)
多线程中乐观锁的执行流程

实现

Atomic*

解决线程不安全问题

CAS底层实现原理
java层面CAS的实现是Unsafe类
Unsafe类调用了C++的本地方法,通过调用操作系统的Atomic::cmpxchg(原子指令)来实现CAS操作

乐观锁性能比较高

CAS的缺点

带来ABA问题

AtomicInteger 是存在ABA问题
A:预期旧值,B表示新值

eg:
ABA问题示例

ABA问题解决方案

引入版本号,每次操作完(修改)+1(更新版本号)

AtomicStampedReference(无ABA问题)
AtomicReference(有ABA问题)

eg:

Integer 高速缓存(-128~127)
设置应用程序的参数:-D

设置Integer高速缓存的最大值:
设置Integer高速缓存的最大值

悲观锁

它认为通常情况下会出现并发冲突,所以在一开始就会加锁

synchronized 是悲观锁

共享锁

一把锁可以被多个程序拥有

读写锁中的读锁就是共享锁

独占锁(非共享锁)

一把锁只能被一个线程拥有

synchronized 是非共享锁

读写锁中的写锁就是非共享锁

读写锁

ReentrantReadWriteLock

将一把锁分成两个,读锁(读数据)、写锁

读锁是可以被多个线程同时拥有的,而写锁只能被一个线程拥有

优点

锁的力度更小,性能更高

注意

读写锁中的读锁和写锁是互斥的(防止同时读写所产生的脏数据)

公平锁

eg:new ReentrantLock(true)
锁的获取顺序必须和线程访问的先后顺序保持一致

优点

执行有序,执行结果可预期

非公平锁

eg:new ReentrantLock(false)/new ReentrantLock()/synchronized
锁的获取顺序和线程访问的先后顺序无关(默认锁策略)

优点

性能更高

自旋锁

synchronized轻量级锁时

通过死循环一直尝试获取锁

while{
	if(尝试获取锁){
		return;
	}
}

缺点

如果发生死锁,则会一直自旋(循环),所以会带来一定的额外开销,消耗系统性能

可重入锁

当一个线程获取一个锁之后,可以重复的进入

		synchronized (lock){
            System.out.println("第一次进入锁");
            synchronized (lock){
                System.out.println("第二次进入锁");
            }
        }

问题

乐观锁和悲观锁具体怎么实现呢?

  • 乐观锁:CAS—>Atomic*
    CAS 组成是由V(内存值)、A(预期旧值)、B(新值),执行时,使用V==A对比,若结果为true则表明没有并发冲突,可以直接修改,否则不能修改
    CAS是通过调用C++实现提供的Unsafe中的本地方法CompareAndSwapXXX来实现的,C++是通过操作系统的原子指令(Atomic::cmpxchg)来实现的
  • 悲观锁:
    synchronized
  • Java层面是将锁的ID存放到对象头来实现的
    偏向锁的实现:
    在对象头中有一个偏向锁ID,可以把自己的线程ID放到偏向锁ID中,每次当有线程访问时,就拿到当前访问它的线程ID和对象头中的偏向锁ID进行对比,如果相等,则表示这个线程拥有了这把锁,直接执行相应代码,否则表示这把锁不属于这个线程,这时,通过自旋,来尝试获取这把锁
  • synchronized在JVM层面是通过监视器锁来实现的
  • synchronized在操作系统层面是通过互斥锁来实现的

java的synchronized是怎么实现的?

  • synchronized来修饰代码块、静态方法、普通方法来实现加锁
  • 在写完synchronized标识后,JVM编译器在生成代码时,会加入一个监视器锁的进入和退出,监视器锁是通过调用操作系统的互斥锁来实现的

synchronized的锁优化?

  • 锁优化(锁消除)
  • JDK1.6锁升级的过程
    无锁—>(第一个线程第一次访问时升级为)偏向锁(将线程ID存储在对象头中的偏向锁标识中)—>轻量级锁(自旋)—>重量级锁(实现用户内存到系统内存的切换(性能低))
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值