java可重入锁(java可重入锁原理)

今天给各位分享java可重入锁的知识,其中也会对java可重入锁原理进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

1、java reentrantlock原理2、java的可重入锁用在哪些场合3、Java锁有哪些种类,以及区别4、什么是重入锁和AQS5、java 可重入锁如何避免死锁的?6、005 锁的重入性 | 重入锁

java reentrantlock原理

你可以参考一下下面的自定义实现:

通过判断线程的方式可以实现可重入锁的构造

public class LockTest03 {

ReLock lock = new ReLock();

public void a() throws InterruptedException {

lock.lock();

System.out.println(lock.getHoldCount());

doSomething();

lock.unlock();

System.out.println(lock.getHoldCount());

}

//不可重入

public void doSomething() throws InterruptedException {

lock.lock();

System.out.println(lock.getHoldCount());

//……………….

lock.unlock();

System.out.println(lock.getHoldCount());

}

public static void main(String[] args) throws InterruptedException {

LockTest03 test = new LockTest03();

test.a();

Thread.sleep(1000);

System.out.println(test.lock.getHoldCount());

}

}

// 可重入锁

class ReLock{

//是否占用

private boolean isLocked = false;

private Thread lockedBy = null; //存储线程

private int holdCount = 0;

//使用锁

public synchronized void lock() throws InterruptedException {

Thread t = Thread.currentThread();

while(isLocked lockedBy != t) {

wait();

}

isLocked = true;

lockedBy = t;

holdCount ++;

}

//释放锁

public synchronized void unlock() {

if(Thread.currentThread() == lockedBy) {

holdCount –;

if(holdCount ==0) {

isLocked = false;

notify();

lockedBy = null;

}

}

}

public int getHoldCount() {

return holdCount;

}

}

有一个上下文的概念。

java可重入锁(java可重入锁原理)

java的可重入锁用在哪些场合

用在只能单线程处理的地方呗,一般来说,如果一个方法被多个线程调用,方法里有修改类变量就需要锁了,因为在线程运行时,类变量被拷贝到线程专用的缓存,然后再拷贝回程序内存,如果同时有多个线程做上述动作,最后一个线程改变后的值就会覆盖其他线程做的修改。

Java锁有哪些种类,以及区别

一、公平锁/非公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁。

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。

对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。

对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。

二、可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。

对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。

对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。

synchronized void setA() throws Exception{

Thread.sleep(1000);

setB();

}

synchronized void setB() throws Exception{

Thread.sleep(1000);

}

上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。

三、独享锁/共享锁

独享锁是指该锁一次只能被一个线程所持有。

共享锁是指该锁可被多个线程所持有。

对于Java

ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。

读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。

独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。

对于Synchronized而言,当然是独享锁。

四、互斥锁/读写锁

上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。

互斥锁在Java中的具体实现就是ReentrantLock

读写锁在Java中的具体实现就是ReadWriteLock

五、乐观锁/悲观锁

乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。

悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。

乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。

从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。

悲观锁在Java中的使用,就是利用各种锁。

乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。

六、分段锁

分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。

我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。

但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。

分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

七、偏向锁/轻量级锁/重量级锁

这三种锁是指锁的状态,并且是针对Synchronized。在Java

5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。

偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。

轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。

重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

八、自旋锁

在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

典型的自旋锁实现的例子,可以参考自旋锁的实现

什么是重入锁和AQS

什么是重入锁

java.util.concurrent.locks.ReentrantLock

ReenTrantLock独有的能力:

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

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

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

非重入锁

当A方法获取锁去锁住一段需要做原子性操作的B方法时,如果这段B方法又需要锁去做原子性操作,那么A方法就必定要与B方法出现死锁。这种会出现问题的重入一把锁的情况,叫不可重入锁。

lock的过程:

   首先尝试获取资源,如果当前状态为0,表示没有线程占有锁,设置该线程为独占模式,使用CAS设置状态,否则如果当前线程和独占线程是一个线程,修改状态值,否则返回false。

  若获取资源失败,则通过addWaiter -(aqs)方法创建一个节点并放在CLH队列的尾部。head tail未初始化会创建虚拟节点同时指向

为什么 AQS 需要一个虚拟 head 节点

每个节点都需要设置前置Node 的 waitStatus  状态(这个状态为是为了保证数据一致性),防止重复释放操作。而第一个节点是没有前置节点的,所以需要创建一个虚拟节点。

  逐步去执行CLH队列中的线程,当前线程会公平性的阻塞一直到获取锁为止,返回线程在等待的过程中还是否中断过。

unlock的过程

一次unlock操作需要修改状态位,然后唤醒节点。整个释放操作也是使用unpark()来唤醒队列最前面的节点。其实lock中比较重要的也就是lock和release,它们又和AQS联系紧密,下面会单独谈谈AQS的重要方法。

Condition的await和signal

wait和notify/notify VS await和signal

Condition能够支持不响应中断,而通过使用Object方式不支持;

Condition能够支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个;

Condition能够支持超时时间的设置,而Object不支持

对标Object的wait方法

void await() throws InterruptedException:当前线程进入等待状态,如果其他线程调用condition的signal或者signalAll方法并且当前线程获取Lock从await方法返回,如果在等待状态中被中断会抛出被中断异常;

long awaitNanos(long nanosTimeout):当前线程进入等待状态直到被通知,中断或者超时;

boolean await(long time, TimeUnit unit)throws InterruptedException:同第二种,支持自定义时间单位

boolean awaitUntil(Date deadline) throws InterruptedException:当前线程进入等待状态直到被通知,中断或者到了某个时间

对标Object的notify/notifyAll方法

void signal():唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,如果在同步队列中能够竞争到Lock则可以从等待方法中返回。

void signalAll():与1的区别在于能够唤醒所有等待在condition上的线程

如图所示,ConditionObject是AQS的内部类,因此每个ConditionObject能够访问到AQS提供的方法,相当于每个Condition都拥有所属同步器的引用。

调用condition.await方法的线程必须是已经获得了lock,也就是当前线程是同步队列中的头结点。调用该方法后会使得当前线程所封装的Node尾插入到等待队列中。

如图,线程awaitThread先通过lock.lock()方法获取锁成功后调用了condition.await方法进入等待队列,而另一个线程signalThread通过lock.lock()方法获取锁成功后调用了condition.signal或者signalAll方法,使得线程awaitThread能够有机会移入到同步队列中,当其他线程释放lock后使得线程awaitThread能够有机会获取lock,从而使得线程awaitThread能够从await方法中退出执行后续操作。如果awaitThread获取lock失败会直接进入到同步队列。

// 线程已被取消

    static final int CANCELLED =  1;

    // 当前线程的后继线程需要被unpark(唤醒)

    // 一般发生情况是:当前线程的后继线程处于阻塞状态,而当前线程被release或cancel掉,因此需要唤醒当前线程的后继线程。

    static final int SIGNAL    = -1;

    // 在Condition休眠状态,在等待Condition唤醒

    static final int CONDITION = -2;

    // (共享锁)其它线程获取到“共享锁”,对应的waitStatus的值

    static final int PROPAGATE = -3;

volatile int waitStatus;

———————

/**

    * 这个方法也就是lock()方法的关键方法。tryAcquire获得资源,返回true,直接结束。若未获取资源,新建一个节点插入队尾,

*addWaiter用于添加节点,也就是把当前线程对应的节点插入CLH队列的尾部。

    * @param arg the acquire argument.  This value is conveyed to

    *        {@link #tryAcquire} but is otherwise uninterpreted and

    *        can represent anything you like.

    */

    public final void acquire(int arg) {

        if (!tryAcquire(arg) //获取资源立刻结束

            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//没有被中断过,也结束

            selfInterrupt();

    }

———————

protected final boolean tryAcquire(int acquires) {

            final Thread current = Thread.currentThread();

            int c = getState();

            if (c == 0) {

                if (!hasQueuedPredecessors()

                    compareAndSetState(0, acquires)) {

                    setExclusiveOwnerThread(current);

                    return true;

                }

            }

            else if (current == getExclusiveOwnerThread()) { //判断是否持有锁的是自己,重入

                int nextc = c + acquires;

                if (nextc 0)

                    throw new Error(“Maximum lock count exceeded”);

                setState(nextc);

                return true;

            }

            return false;

        }

———————

  * 非公平锁

    */

    static final class NonfairSync extends Sync {

        private static final long serialVersionUID = 7316153563782823691L;

        /**

        * Performs lock.  Try immediate barge, backing up to normal

        * acquire on failure.

        */

        final void lock() {

            if (compareAndSetState(0, 1))//CAS设置当前为0 的时候上锁

                setExclusiveOwnerThread(Thread.currentThread());

            else

                acquire(1);//否则尝试获得锁。

        }

        protected final boolean tryAcquire(int acquires) {

            return nonfairTryAcquire(acquires);

        }

    }

    /**

    * 公平锁

    */

    static final class FairSync extends Sync {

        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {

            acquire(1);

        }

        /**

        *

        */

        protected final boolean tryAcquire(int acquires) {

            final Thread current = Thread.currentThread();

            int c = getState();

            if (c == 0) {

                if (!hasQueuedPredecessors()

                    compareAndSetState(0, acquires)) {//没有前驱节点并且CAS设置成功

                    setExclusiveOwnerThread(current);//设置当前线程为独占线程

                    return true;

                }

            }

            else if (current == getExclusiveOwnerThread()) {//这里和非公平锁类似

                int nextc = c + acquires;

                if (nextc 0)

                    throw new Error(“Maximum lock count exceeded”);

                setState(nextc);

                return true;

            }

            return false;

        }

    }

java 可重入锁如何避免死锁的?

synchronized标记的同步是要绑定一个对象的,不写的话实际上实际上就是synchronized(this),即绑定当前对象,这个this对象就是锁(synchronized中可以认为就是监视器),当LoggingWidget执行dosomething的时候获得了这把锁(this),那么他去调用父类(Widget)的dosomthing的时候,父类的dosomething方法也要得到这个锁(this),但是子类的这个方法还没有运行完毕,所以还持有这个锁,父类方法在等,子类不释放锁还拼命的让父类方法执行,却不知道父类方法在眼巴巴的等着这个锁,这样就死锁了······

005 锁的重入性 | 重入锁

当 一个进程 获得到某个对象的锁后,在其他地方,又需要获得该对象的锁,此时,可以进入,而不是阻塞。

Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁

重入的特性, 发生在同一个线程本身,而且是获得锁后的时间内,再次需要获得锁,不阻塞,直接进入 。

上述,两个synchronized关键词,作用都是一个对象d1(确保是同一个锁了),a方法获得锁,在持有锁期间,需要获得b方法的锁(同一个锁),重入不阻塞。

看是否能够获得锁,需要明确是否是同一个对象的锁,以及是否可重入,如下,

执行顺序(可能)先输出a,等待1秒后,输出b,为什么不是同时输出ab?

因为第一个线程先获得到锁后,对于第二个线程而言,因为是一个锁对象,所以等待,当第一个线程执行完毕后释放锁,第二个线程才能获得锁。 可重入性针对是同一个线程而言,多个线程的情况,竞争获得锁

关于java可重入锁和java可重入锁原理的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

本文来自投稿,不代表【】观点,发布者:【

本文地址: ,如若转载,请注明出处!

举报投诉邮箱:253000106@qq.com

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2024年4月3日 00:33:16
下一篇 2024年4月3日 00:43:44

相关推荐

  • 深入java虚拟机pdf,深入java虚拟机 中村成洋 pdf

    在linux环境下,java怎么实现从word格式转换为pdf格式 //设置当前使用的打印机,我的Adobe Distiller打印机名字为 Adobe PDF wordCom.setProperty( ActivePrinter , new Variant( Adobe PDF ));//设置printout的参数,将word文档打印为postscript…

    2024年5月23日
    4400
  • java截取指定长度字符串,java截取指定字符串之后的

    java中如何截取字符串中的指定一部分 第一个参数是开始截取的字符位置。(从0开始)第二个参数是结束字符的位置+1。(从0开始)indexof函数的作用是查找该字符串中的某个字的位置,并且返回。 int end);截取s中从begin开始至end结束时的字符串,并将其赋值给s;split讲解:java.lang.string.split split 方法 将…

    2024年5月23日
    4200
  • java绑定一个端口,java使用端口

    java如何多个service共用一个端口 你如果有多个项目的话,你可以把多个项目放到一个tomcat里面,这样端口相同使用项目名称来进行区分项目。你如果非要使用同一个,你也可以配置不同的域名导向不同的项目。就是访问的域名不同转接到的项目不同。 如果需要同时启动多个程序,要么修改tomcat的配置文件中的监听端口。要么修改jar包程序的监听端口。不能在一台服…

    2024年5月23日
    3500
  • java多线程并发编程基础,Java多线程并发执行返回

    电脑培训分享Java并发编程:核心理论 电脑培训发现本系列会从线程间协调的方式(wait、notify、notifyAll)、Synchronized及Volatile的本质入手,详细解释JDK为我们提供的每种并发工具和底层实现机制。 人们开始意识到了继承的众多缺点,开始努力用聚合代替继承。软件工程解决扩展性的重要原则就是抽象描述,直接使用的工具就是接口。接…

    2024年5月23日
    4700
  • 自学java找工作,自学java找工作需要包装简历吗

    自学java学多久可以找到工作 1、自学Java至少需要一年以上的时间才能达到找工作的水平。报班培训四到六个月的时间就可以找到一份不错的工作。 2、自学Java至少需要一年以上的时间才能达到找工作的水平。 3、如果要想找到一份Java相关的工作,需要至少学习5-6个月时间才能就业。Java开发需要掌握一些基础的编程语言知识,比如掌握面向对象的编程思想、基本的…

    2024年5月23日
    4300
  • java左移右移,java 左移

    java位移问题 1、思路:直接用Integer类的bit运算操作。 2、移位操作:左移:向左移位,符号后面的数字是移了多少位,移的位用0补齐,例如2进制数01111111左移一位后变为11111110,移位是字节操作。 3、Java 位运算 Java 位运算[转]一,Java 位运算表示方法: 在Java语言中,二进制数使用补码表示,最高位为符号位,正数的…

    2024年5月23日
    4200
  • java技术规范,java规范性要求

    现在主流的JAVA技术是什么? java最流行开发技术程序员必看 1 、Git Git一直是世界上最受欢迎的Java工具之一,也是Java开发人员最杰出的工具之一。Git是一个开源工具,是-种出色的分布式版本控制解决方案。 (1).Java基础语法、数组、类与对象、继承与多态、异常、范型、集合、流与文件、反射、枚举、自动装箱和注解。(2).Java面向对象编…

    2024年5月23日
    4000
  • javasocket编程,Java socket编程中,禁用nagle算法的参数

    Java进行并发多连接socket编程 1、Java可利用ServerSocket类对外部客户端提供多个socket接口。基本的做法是先创建一个ServerSocket实例,并绑定一个指定的端口,然后在这个实例上调用accept()方法等待客户端的连接请求。 2、Socket socket=server.accept(0;Thread handleThrea…

    2024年5月23日
    4600
  • java死亡,java死代码是什么意思

    我的世界传送回死亡点指令是什么? 1、下面就让我们一起来了解一下吧:我的世界回到死的地方的指令是输入/back,就可以回到死亡地点了,当然也可以看信标,因为死亡后会有一道光集中在死亡点,只要循着光就可以找到目的地了。 2、在服务器中的指令 首先打开指令台,在指令行输入“/back”就可以回到自己的死亡地点了。在单人游戏中的指令 在单人游戏中,您无法直接返回到…

    2024年5月23日
    4800
  • myeclipse能部署java工程么,myeclipse支持jdk18

    myeclipse如何建java文件 1、点击【File】—【New】–【Class】在如下界面,输入Class的名字,如Test,点击【Finish】。Test.java文件创建成功。 2、点击【File】—【New】–【Class】 在如下界面,输入Class的名字,如Test,点击【Finish】。 Te…

    2024年5月23日
    3900

发表回复

登录后才能评论



关注微信