博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发编程-可重入锁
阅读量:7246 次
发布时间:2019-06-29

本文共 2622 字,大约阅读时间需要 8 分钟。

可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍可以获取该锁而不受影响。

在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁。

public class Test implements Runnable{
public synchronized void get(){
System.out.println(Thread.currentThread().getId()); set(); } public synchronized void set(){
System.out.println(Thread.currentThread().getId()); } @Override public void run() {
get(); } public static void main(String[] args) {
Test ss=new Test(); new Thread(ss).start(); new Thread(ss).start(); new Thread(ss).start(); }}

两个例子最后的结果都是正确的,即 同一个线程id被连续输出两次。

结果如下:
Threadid: 8
Threadid: 8
Threadid: 10
Threadid: 10
Threadid: 9
Threadid: 9
可重入锁最大的作用是避免死锁
我们以自旋锁作为例子

public class SpinLock {
private AtomicReference owner =new AtomicReference<>; public void lock(){
Thread current = Thread.currentThread(); while(!owner.compareAndSet(null, current)){
} } public void unlock (){
Thread current = Thread.currentThread(); owner.compareAndSet(current, null); }}

对于自旋锁来说,

1、若有同一线程两次调用lock() ,会导致第二次调用lock位置进行自旋,产生了死锁
说明这个锁并不是可重入的。(在lock函数内,应验证线程是否为已经获得锁的线程)
2、若1问题已经解决,当unlock()第一次调用时,就已经将锁释放了。实际上不应释放锁。
(采用计数次进行统计)
修改之后,如下:

public class SpinLock1 {
private AtomicReference owner =new AtomicReference<>; private int count =0; public void lock(){
Thread current = Thread.currentThread(); if(current==owner.get()) {
count++; return ; } while(!owner.compareAndSet(null, current)){
} }public void unlock (){
Thread current = Thread.currentThread(); if(current==owner.get()){
if(count!=0){
count--; }else{
owner.compareAndSet(current, null); } } }}

该自旋锁即为可重入锁。

可重入锁机制:每个锁都关联一个请求计数器和一个占有他的线程,当请求计数器为0时,这个锁可以被认为是unhled的,当一个线程请求一个unheld的锁时,JVM记录锁的拥有者,并把锁的请求计数加1,如果同一个线程再次请求这个锁时,请求计数器就会增加,当该线程退出syncronized块时,计数器减1,当计数器为0时,锁被释放。

public class Widget {
public synchronized void doSomething() {
...}}public class LoggingWidget extends Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething"); super.doSomething(); }}

如果没有Java锁的可重入性,当一个线程获取LoggingWidget的doSomething()代码块的锁后,这个线程已经拿到了LoggingWidget的锁,当调用父类中的doSomething()方法的时,JVM会认为这个线程已经获取了LoggingWidget的锁,而不能再次获取,从而无法调用Widget的doSomething()方法,从而造成死锁。从中我们也能看出,java线程是基于“每线程(per-thread)”,而不是基于“每调用的(per-invocation)”的,也就是说java为每个线程分配一个锁,而不是为每次调用分配一个锁。

 

转载于:https://www.cnblogs.com/suxuan/p/4948748.html

你可能感兴趣的文章
我的Android进阶之旅------>Android安全退出应用程序的几种方式
查看>>
我的Android进阶之旅------>Android中StateListDrawable支持的状态
查看>>
Java运行时内存划分
查看>>
To do List
查看>>
live555运行时报错:StreamParser internal error ( 86451 + 64000 > 150000)
查看>>
常识积累
查看>>
HTML 页面元素介绍
查看>>
DevOps最佳工具集实践
查看>>
面试题5
查看>>
将word内容追击到新建WORD文档
查看>>
tree
查看>>
11gR2集群件任务角色分离(Job Role Separation)简介
查看>>
linux shell数据重定向
查看>>
Django数据库表中多对多关系ROM操作
查看>>
03-字符编码-读写模式-课堂笔记
查看>>
如何测试wifi的吞吐率
查看>>
分布式、中间件和消息队列、集群
查看>>
android的5个进程等级
查看>>
分享职场心得《8》
查看>>
gets、scanf和getchar之间的区别
查看>>