ReentrantLock的探究

锁及锁粒度的详细比喻:

参考:https://www.cnblogs.com/nancymake/p/6252536.html

今天看到一篇文章,很详细的描述了锁和锁粒度的概念,如下:

为什么要加锁?加锁是为了防止不同的线程访问同一共享资源造成混乱。
打个比方:人是不同的线程,卫生间是共享资源
你在上洗手间的时候肯定要把门锁上吧,这就是加锁,只要你在里面,这个卫生间就被锁了,只有你出来之后别人才能用。想象一下如果卫生间的门没有锁会是什么样?


什么是加锁粒度呢?所谓加锁粒度就是你要锁住的范围是多大。
比如你在家上卫生间,你只要锁住卫生间就可以了吧,不需要将整个家都锁起来不让家人进门吧,卫生间就是你的加锁粒度。


怎样才算合理的加锁粒度呢?
其实卫生间并不只是用来上厕所的,还可以洗澡,洗手。这里就涉及到优化加锁粒度的问题。
你在卫生间里洗澡,其实别人也可以同时去里面洗手,只要做到隔离起来就可以,如果马桶,浴缸,洗漱台都是隔开相对独立的,实际上卫生间可以同时给三个人使用,

当然三个人做的事儿不能一样。这样就细化了加锁粒度,你在洗澡的时候只要关上浴室的门,别人还是可以进去洗手的。如果当初设计卫生间的时候没有将不同的功能区域划分
隔离开,就不能实现卫生间资源的最大化使用。这就是设计架构的重要性

用户态和核心态的区别:

参考:https://www.cnblogs.com/Allen-rg/p/7171105.html

 

Java面试题:详细说明一下可重入锁ReentrantLock的原理

转:https://baijiahao.baidu.com/s?id=1594800969528243663&wfr=spider&for=pc

ReentrantLock和synchronized的区别

可重入性:

从名字上理解,ReenTrantLock的字面意思就是再进入的锁,其实synchronized关键字所使用的锁也是可重入的,两者关于这个的区别不大。两者都是同一个线程每进入一次,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。

锁的实现:

Synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的,有什么区别,说白了就类似于操作系统来控制实现和用户自己敲代码实现的区别。前者的实现是比较难见到的,后者有直接的源码可供阅读。

性能的区别:

在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。

synchronized

功能区别:(synchronized锁使用方便)

便利性:很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。

Lock有更灵活的锁定方式:公平锁与不公平锁,而synchronized永远是公平的。

锁的细粒度和灵活度:很明显ReenTrantLock优于Synchronized(锁的细粒度是指:Lock接口的实现允许锁在不同的作用范围内获取和释放并允许以任何顺序获取和释放多个锁从而支持使用这种技术。而Lock因为是方法调用,可以跨方法,灵活性更大。灵活度体现在lock锁有公平锁和不公平锁两种的锁定方式,synchronized的话,锁的范围是整个方法或synchronized块部分;)

//lock锁跨方法实例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class 可重入锁的练习 {
	private static Lock lock;

	public static void main(String[] args) {
		P1();
		// lock的使用
		try {
			P2();
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			P3();
		}
	}

	public static void P1() {
		lock = new ReentrantLock();
		lock.lock();
		System.out.println("开始加锁......");
	}

	public static void P2() {
		System.out.println("加锁中........");
	}

	public static void P3() {
		System.out.println("锁关闭了");
		lock.unlock();
	}

	public static void print123() {
		synchronized (Object.class) {
			System.out.println("123");
			synchronized (Object.class) {
				System.out.println("456");
			}

		}
	}

}

ReentrantLock默认情况下为不公平锁。

ReenTrantLock独有的能力:

1.      ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。

2.      ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

3.      ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。

参考:http://www.iteedu.com/plang/java/superjava/threadsafe/lockInterruptibly.htm

中断介绍

中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序。虽然初次看来它可能显得简单,但是,你必须进行一些预警以实现期望的结果。

从上面的介绍知道,interrupt()并不会使线程停止运行,那如何停止线程呢?

中断线程最好的,最受推荐的方式是,使用共享变量(shared
variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务

class Example2 extends Thread {
    volatile boolean stop = false;
    public static void main(String args[]) throws Exception {
        Example2 thread = new Example2();
        System.out.println("Starting thread...");
        thread.start();
        Thread.sleep(3000);
        System.out.println("Asking thread to stop...");
        thread.stop = true;
        Thread.sleep(3000);
        System.out.println("Stopping application...");
        //System.exit( 0 );
    }
    public void run() {
        while (!stop) {
            System.out.println("Thread is running...");
            long time = System.currentTimeMillis();
            while ((System.currentTimeMillis() - time < 1000) && (!stop)) {
            }
        }
        System.out.println("Thread exiting under request...");
    }
}

响应中断是什么意思?

比如A、B两线程去竞争锁,A得到了锁,B等待,但是A有很多事情要处理,所以一直不返回。B可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。在这种情况下,synchronized的做法是,B线程中断自己(或者别的线程中断它),我不去响应,继续让B线程等待,你再怎么中断,我全当耳边风。而lockInterruptibly()的做法是,B线程中断自己(或者别的线程中断它),ReentrantLock响应这个中断,不再让B等待这个锁的到来。有了这个机制,使用ReentrantLock时死锁了线程可以中断自己来解除死锁。

try {
    lock.lockInterruptibly();
    //操作
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    lock.unlock();
}
private ReentrantLock lock = new ReentrantLock(); //参数默认false,不公平锁
private ReentrantLock lock = new ReentrantLock(true); //公平锁
try {
    lock.lock(); //如果被其它资源锁定,会在此等待锁释放,达到暂停的效果
   //操作
} finally {
    lock.unlock();
}

不公平锁与公平锁的区别:

公平情况下,操作会排一个队按顺序执行,来保证执行顺序。(会消耗更多的时间来排队)
不公平情况下,是无序状态允许插队,jvm会自动计算如何处理更快速来调度插队。(如果不关心顺序,这个速度会更快)

ReenTrantLock实现的原理:

在网上看到相关的源码分析,本来这块应该是本文的核心,但是感觉比较复杂就不一一详解了,简单来说,ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态。想尽办法避免线程进入内核的阻塞状态是我们去分析和理解锁设计的关键钥匙。

场景4:可中断锁。

ReentrantLock可重入锁的原理及使用场景

https://blog.csdn.net/zhousenshan/article/details/53026785

可重入概念

若一个程序或子程序可以“安全的被并行执行(Parallel computing)”,则称其为可重入(reentrant或re-entrant)的。即当该子程序正在运行时,可以再次进入并执行它(并行执行时,个别的执行结果,都符合设计时的预期)。可重入概念是在单线程操作系统的时代提出的。

Synchronized与ReentrantLock区别总结(简单粗暴,一目了然)

参考较好:https://blog.csdn.net/zxd8080666/article/details/83214089

1.Synchronized
Synchronized进过编译,会在同步块的前后分别形成monitorentermonitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

参考:https://blog.csdn.net/hunterliy/article/details/53954197?utm_source=blogxgwz2

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值