JAVA线程锁

1.乐观锁

乐观锁是一种乐观思想,每次读取数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断在此期间别人有没有去更新这条数据。乐观锁只能防止脏读后数据的提交
并不能解决脏读。
实现方式:

  1. 加version字段,每一次的操作都会更新version,提交时如果version前后不相等,停止本次提交。
  2. CAS 算法
    compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,所以也叫非阻塞同步。
    CAS 算法涉及到三个操作数:
    • 需要读写的内存值 V
    • 进行比较的值 A
    • 拟写入的新值 B
  • 当且仅当 V 的值等于 A 时,CAS 通过原子方式用新值 B 来更新 V 的值,否则不会执行任何操作(比较和替换是一个 native 原子操作)。一般情况下,这是一个自旋操作,即不断的重试。

2.悲观锁

悲观锁是就是悲观思想,遇到并发写的可能性高,每次去读数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁。

  • java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如 RetreenLock。

3.自旋锁

当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
自旋锁需要设定一个自旋等待的最大时间,因为自旋锁需要让CPU做无用功,如果一直获取不到锁,现在就会一直占用CPU做无用功。

  • 当锁的竞争不激烈,使用自旋锁,因为自旋的消耗小于线程阻塞挂起再唤醒的操作的消耗。
  • 当锁的竞争激烈,就不适合使用自旋锁,,因为自旋锁在获取锁前一直都是占用 CPU做无用功,导致CPU性能的浪费。

4.ReentrantLock

继承Lock接口并实现接口中定义的方法,它是一种可重入的锁,不仅能够完成synchronized能完成的工作,还能提供了许多避免多线程死锁方法。

public class ReentrantLockTest {

	public static final Lock lock = new ReentrantLock();
	public static void main(String[] args) {
		new Thread(() -> test(),"线程A").start();
		new Thread(() -> test(),"线程B").start();
	}
	
	public static void test() {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName()+"获取了锁");
			Thread.sleep(2000);
		}
		catch(Exception e) {
			e.printStackTrace();
		}finally {
			System.out.println(Thread.currentThread().getName()+"释放了锁");
			lock.unlock();
		}
	}
}
/**
* 执行结果: 
* 线程A获取了锁
* 线程A释放了锁
* 线程B获取了锁
* 线程B释放了锁
*/

5.Synchronized

synchronized 它可以把任意一个非 NULL 的对象当作锁。他属于独占式的悲观锁,同时属于可重入锁。

  1. 作用于方法时,锁住的是对象的实例(this);
  2. 当作用于静态方法时,锁住的是Class实例,又因为Class的相关数据存储在永久带PermGen(jdk1.8 则是 metaspace),永久带是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程;
  3. synchronized 作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。它有多个队列,当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。

6. 公平锁与非公平锁

  • 公平锁
    加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得。
  • 非公平锁
    加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待。

7.共享锁和独占锁

  • 独占锁
    每次只能有一个线程能持有锁。
    独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。
  • 共享锁
    共享锁则允许多个线程同时获取锁,并发访问共享资源。
    乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。

8.同步锁与死锁

  • 同步锁
    在同一时间内只允许一个线程访问共享数据。 Java 中可以使用 synchronized 关键字来取得一个对象的同步锁。

  • 死锁
    何为死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。
    线程1持有锁A,线程2持有锁B,线程1想获取锁B,线程2想获取锁A,循环等待。

    发生死锁的条件:
    互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

    请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

    不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。

    循环等待条件: 若干进程间形成首尾相接循环等待资源的关系。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值