前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >多线程方向的锁

多线程方向的锁

作者头像
suveng
发布于 2019-09-17 02:10:26
发布于 2019-09-17 02:10:26
40500
代码可运行
举报
运行总次数:0
代码可运行

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/qq_37933685/article/details/80767809

个人博客:https://suveng.github.io/blog/​​​​​​​

多线程方向的锁

注意:

环境说明:

简单锁

简单的给资源加把锁;以下的所有代码实现都使用同一个类文件.

Counter.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Counter {
    private OrdinaryLock lock = new OrdinaryLock();
    private int count = 0;

    public int inc() throws InterruptedException {
        Thread.sleep(2000);
        lock.lock();//注释掉看区别:TODO
        System.out.println(Thread.currentThread().getId() + "前..." + this.count);
        this.count++;
        System.out.println(Thread.currentThread().getId() + "后..." + this.count);
        lock.unlock();//注释掉看区别:TODO
        return count;
    }
    public static void main(String[] args) throws InterruptedException {
        final Counter counter = new Counter();
        final ReentrantClazz reentrantClazz = new ReentrantClazz();
        final ReentrantLock reentrantLock =new ReentrantLock();
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                public void run() {

                    try {
                        counter.inc();//OrdinaryLock实例
                        //reentrantClazz.outer();//重入锁实例 这是重入锁的我现在先注释掉

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
  class OrdinaryLock {
    private boolean isLocked = false;

    public synchronized void lock() throws InterruptedException {
        while (isLocked) {    //不用if,而用while,是为了防止假唤醒
            wait();
        }
        isLocked = true;
    }

    public synchronized void unlock() {
        isLocked = false;
        notify();
    }
}

如上代码,我先把inc()的lock注释掉;运行结果如下:

控制台打印无序,而且前后相差不止1;具体逻辑看inc()方法.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
12...0
16...0
20...1
17...0
17...4
18...0
15...0
14...0
13...0
11...0
13...8
14...7
15...6
18...5
20...3
16...2
19...1
19...10
12...1
11...9

Process finished with exit code 0

当我加了普通锁之后,把注释放开,就会有序,而且保证线程加减前后相差是1.运行结果如下所示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
11...0
11...1
13...1
13...2
14...2
14...3
12...3
12...4
15...4
15...5
16...5
16...6
17...6
17...7
19...7
19...8
18...8
18...9
20...9
20...10

Process finished with exit code 0

这就是加锁的魅力,但是同时也会损失效率和响应速度.

重入锁

重进入是指任意线程在获取到锁之后,再次获取该锁而不会被该锁所阻塞。关联一个线程持有者+计数器,重入意味着锁操作的颗粒度为“线程”。

重入锁的实现方式:每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁

接下来先演示没有重入特性的普通锁,当内部方法想要获取锁的时候就会陷入死锁.

代码如下,在Counter.java里面新增内部类,然后在main()方法中调用这个类的outer方法.outer()调用inner(),inner()也尝试获取锁.结果会陷入死锁

synchronized:可重入锁; java.util.concurrent.locks.ReentrantLock:可重入锁;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//重入示例,outer调用inner 方法
//lock可以使用origin lock 或者reentrant lock
//使用origin lock 会造成死锁 reentrant lock 不会
class ReentrantClazz {
    OrdinaryLock lock = new OrdinaryLock();//:TODO 修改为reentrantLock 再运行即可

    public void outer() throws InterruptedException {
        lock.lock();
        System.out.println("进入outter");
        inner();
        lock.unlock();
    }

    public void inner() throws InterruptedException {
        System.out.println("进入inner");
        lock.lock();
        //do something
        lock.unlock();
    }
}

运行结果如下,

没有结束,只是获取并没有释放,因为上面使用的lock是OrdinaryLock,接下来把这个换成可重入锁ReentrantLock,

ReentrantClazz类中的OrdinaryLock lock = new OrdinaryLock();,换成ReentrantLock lock = new ReentrantLock();

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//可重入的锁
class ReentrantLock {
    private boolean isLocked = false;
    private Thread lockedBy = null;
    private int lockedCount = 0;

    public synchronized void lock()
            throws InterruptedException {
        Thread callingThread = Thread.currentThread();
        while (isLocked && lockedBy != callingThread) {
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = callingThread;
    }

    public synchronized void unlock() {
        if (Thread.currentThread() == this.lockedBy) {
            lockedCount--;
            if (lockedCount == 0) {
                isLocked = false;
                notify();
            }
        }
    }
}

运行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
11进入outter
11线程outer()方法获取了锁
11进入inner
11线程inner()方法获取了锁
11线程inner()方法释放了锁
11线程outer()方法释放了锁
12进入outter
12线程outer()方法获取了锁
12进入inner
12线程inner()方法获取了锁
12线程inner()方法释放了锁
12线程outer()方法释放了锁
13进入outter
13线程outer()方法获取了锁
13进入inner
13线程inner()方法获取了锁
13线程inner()方法释放了锁
13线程outer()方法释放了锁
14进入outter
14线程outer()方法获取了锁
14进入inner
14线程inner()方法获取了锁
14线程inner()方法释放了锁
14线程outer()方法释放了锁
15进入outter
15线程outer()方法获取了锁
15进入inner
15线程inner()方法获取了锁
15线程inner()方法释放了锁
15线程outer()方法释放了锁
16进入outter
16线程outer()方法获取了锁
16进入inner
16线程inner()方法获取了锁
16线程inner()方法释放了锁
16线程outer()方法释放了锁
17进入outter
17线程outer()方法获取了锁
17进入inner
17线程inner()方法获取了锁
17线程inner()方法释放了锁
17线程outer()方法释放了锁
18进入outter
18线程outer()方法获取了锁
18进入inner
18线程inner()方法获取了锁
18线程inner()方法释放了锁
18线程outer()方法释放了锁
19进入outter
19线程outer()方法获取了锁
19进入inner
19线程inner()方法获取了锁
19线程inner()方法释放了锁
19线程outer()方法释放了锁
20进入outter
20线程outer()方法获取了锁
20进入inner
20线程inner()方法获取了锁
20线程inner()方法释放了锁
20线程outer()方法释放了锁

Process finished with exit code 0

很明显每个线程的outer和inner都能获取并释放锁.这就是可重入锁.

GitHub代码地址:https://github.com/1344115844/learning

自旋锁

自旋锁的核心:不放弃时间片。线程获取不到锁,就会被阻塞挂起,等其他线程释放锁的时候,才被唤醒起来。线程挂起和唤醒是需要转入到内核态完成的,这些操作对系统的并发性能会带来影响。其实有时候线程虽然没法立刻获取到锁,但是也可能很快就会获取到锁。JVM采用了一种叫自旋锁的机制,让获取不到锁的线程执行一个空的循环,一段时间后,如果还是没法获取锁,线程才会被挂起。

如果锁竞争不严重的情况下,且任务执行时间不长,那么可以尝试使用自旋锁。

自旋锁可能引起的问题:

  1. 过多占据CPU时间:如果锁的当前持有者长时间不释放该锁,那么等待者将长时间的占据cpu时间片,导致CPU资源的浪费,因此可以设定一个时间,当锁持有者超过这个时间不释放锁时,等待者会放弃CPU时间片阻塞;
  2. 死锁问题:试想一下,有一个线程连续两次试图获得自旋锁(比如在递归程序中),第一次这个线程获得了该锁,当第二次试图加锁的时候,检测到锁已被占用(其实是被自己占用),那么这时,线程会一直等待自己释放该锁,而不能继续执行,这样就引起了死锁。因此递归程序使用自旋锁应该遵循以下原则:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。

代码实现自旋锁的不可重入锁:

BadSpinLock.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author Veng Su 1344114844@qq.com
 * @date 2018/6/18 8:49
 */

import java.util.concurrent.atomic.AtomicReference;
/**
 *@author Veng Su 2018/6/18 8:53
 *不可重入的自旋锁
 **/

public class BadSpinLock {

    AtomicReference<Thread> owner = new AtomicReference<Thread>();//持有自旋锁的线程对象
    public void lock() {
        Thread cur = Thread.currentThread();
        while (!owner.compareAndSet(null, cur)) {
            System.out.println(cur.getId()+ " 自旋中");
        }
        System.out.println(cur.getId()+"线程上锁成功");
    }

    public void unLock() {
        Thread cur = Thread.currentThread();
        if (cur == owner.get()) {
            owner.compareAndSet(cur, null);
            System.out.println(cur.getId()+ " 释放了锁");
        }
    }


}

这里是没有使用count进行线程的获取锁的计数.会陷入死锁

main方法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void main(String[] args) throws InterruptedException {
        final Counter counter = new Counter();
        final ReentrantClazz reentrantClazz = new ReentrantClazz();
        final ReentrantLock reentrantLock =new ReentrantLock();
        final SpinLock spinLock =new SpinLock();
        for (int i = 0; i < 1; i++) {
            new Thread(new Runnable() {
                public void run() {

                    try {
//                        counter.inc();//OrdinaryLock实例
                        reentrantClazz.outer();//重入锁实例
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
    
//重入示例,outer调用inner 方法
//lock可以使用origin lock 或者reentrant lock
//使用origin lock 会造成死锁 reentrant lock 不会
class ReentrantClazz {
    BadSpinLock lock = new BadSpinLock();//:TODO 修改为BadSpinLock 再运行即可

    public void outer() throws InterruptedException {
        System.out.println(Thread.currentThread().getId()+"进入outter");
        lock.lock();
        System.out.println(Thread.currentThread().getId()+"线程outer()方法获取了锁");
        inner();
        lock.unLock();
        System.out.println(Thread.currentThread().getId()+"线程outer()方法释放了锁");

    }

    public void inner() throws InterruptedException {
        System.out.println(Thread.currentThread().getId()+"进入inner");
        lock.lock();
        System.out.println(Thread.currentThread().getId()+"线程inner()方法获取了锁");
        //do something
        lock.unLock();
        System.out.println(Thread.currentThread().getId()+"线程inner()方法释放了锁");
    }
}

运行结果如下:

进入inner方法后就获取不到锁了,这是不可重入锁,造成死锁.

代码实现自旋锁的可重入锁

SpinLock.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author Veng Su 1344114844@qq.com
 * @date 2018/6/18 8:35
 * 可重入的自旋锁
 */
public class SpinLock {
    AtomicReference<Thread> owner = new AtomicReference<Thread>();//持有自旋锁的线程对象
    private int count;//用一个计数器 来做 重入锁获取次数的计数
    public void lock() {
        Thread cur = Thread.currentThread();
        if (cur == owner.get()) {
            count++;
            return;
        }

        while (!owner.compareAndSet(null, cur)) {//当线程越来越多  由于while循环 会浪费CPU时间片,CompareAndSet 需要多次对同一内存进行访问
            //会造成内存的竞争,然而对于X86,会采取竞争内存总线的方式来访问内存,所以会造成内存访问速度下降(其他线程老访问缓存),因而会影响整个系统的性能
            System.out.println(Thread.currentThread().getId()+"线程自旋中....");
        }
    }

    public void unLock() {
        Thread cur = Thread.currentThread();
        if (cur == owner.get()) {
            if (count > 0) {
                count--;
            } else {
                owner.compareAndSet(cur, null);
            }
        }
    }
}

然后把reentrantClazz类的lock换成spinlock;

运行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
11进入outter
11线程outer()方法获取了锁
11进入inner
11线程inner()方法获取了锁
11线程inner()方法释放了锁
11线程outer()方法释放了锁
12进入outter
12线程outer()方法获取了锁
12进入inner
12线程inner()方法获取了锁
12线程inner()方法释放了锁
12线程outer()方法释放了锁
13进入outter
13线程outer()方法获取了锁
13进入inner
13线程inner()方法获取了锁
13线程inner()方法释放了锁
13线程outer()方法释放了锁
14进入outter
14线程outer()方法获取了锁
14进入inner
14线程inner()方法获取了锁
14线程inner()方法释放了锁
14线程outer()方法释放了锁
16进入outter
16线程outer()方法获取了锁
16进入inner
16线程inner()方法获取了锁
16线程inner()方法释放了锁
16线程outer()方法释放了锁
17进入outter
17线程outer()方法获取了锁
17进入inner
17线程inner()方法获取了锁
17线程inner()方法释放了锁
18进入outter
17线程outer()方法释放了锁
18线程outer()方法获取了锁
18进入inner
18线程inner()方法获取了锁
18线程inner()方法释放了锁
18线程outer()方法释放了锁
19进入outter
19线程outer()方法获取了锁
19进入inner
19线程inner()方法获取了锁
19线程inner()方法释放了锁
19线程outer()方法释放了锁
20进入outter
20线程outer()方法获取了锁
20进入inner
20线程inner()方法获取了锁
20线程inner()方法释放了锁
20线程outer()方法释放了锁
15进入outter
15线程outer()方法获取了锁
15进入inner
15线程inner()方法获取了锁
15线程inner()方法释放了锁
15线程outer()方法释放了锁

Process finished with exit code 0

公平锁和非公平锁

ReentrantLock锁的实现分析

ReentrantLock 的公平锁和非公平锁都委托了 AbstractQueuedSynchronizer#acquire 去请求获取。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}12345
  • tryAcquire 是一个抽象方法,是公平与非公平的实现原理所在。
  • addWaiter 是将当前线程结点加入等待队列之中。公平锁在锁释放后会严格按照等到队列去取后续值而非公平锁在对于新晋线程有很大优势
  • acquireQueued 在多次循环中尝试获取到锁或者将当前线程阻塞。
  • selfInterrupt 如果线程在阻塞期间发生了中断,调用 Thread.currentThread().interrupt() 中断当前线程。

ReentrantLock 对线程的阻塞是基于 LockSupport.park(this); (见 AbstractQueuedSynchronizer#parkAndCheckInterrupt)。 先决条件是当前节点有限次尝试获取锁失败。

公平锁和非公平锁在说的获取上都使用到了 volatile 关键字修饰的state字段, 这是保证多线程环境下锁的获取与否的核心。

但是当并发情况下多个线程都读取到 state == 0时,则必须用到CAS技术,一门CPU的原子锁技术,可通过CPU对共享变量加锁的形式,实现数据变更的原子操作。

volatile 和 CAS的结合是并发抢占的关键。

公平锁FairSync

公平锁的实现机理在于每次有线程来抢占锁的时候,都会检查一遍有没有等待队列,如果有, 当前线程会执行如下步骤:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (!hasQueuedPredecessors() &&
    compareAndSetState(0, acquires)) {
    setExclusiveOwnerThread(current);
    return true;
}12345

其中hasQueuedPredecessors是用于检查是否有等待队列的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }1234567

非公平锁NonfairSync

非公平锁在实现的时候多次强调随机抢占:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (c == 0) {
    if (compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
    }
}123456

与公平锁的区别在于新晋获取锁的进程会有多次机会去抢占锁。如果被加入了等待队列后则跟公平锁没有区别。

ReentrantLock锁的释放

ReentrantLock锁的释放是逐级释放的,也就是说在 可重入性 场景中,必须要等到场景内所有的加锁的方法都释放了锁, 当前线程持有的锁才会被释放!

释放的方式很简单, state字段减一即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
protected final boolean tryRelease(int releases) {
    //  releases = 1
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}12345678910111213

ReentrantLock等待队列中元素的唤醒

当当前拥有锁的线程释放锁之后, 且非公平锁无线程抢占,就开始线程唤醒的流程。

通过tryRelease释放锁成功,调用LockSupport.unpark(s.thread); 终止线程阻塞。

见代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void unparkSuccessor(Node node) {
    // 强行回写将被唤醒线程的状态
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    Node s = node.next;
    // s为h的下一个Node, 一般情况下都是非Null的
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 否则按照FIFO原则寻找最先入队列的并且没有被Cancel的Node
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 再唤醒它
    if (s != null)
        LockSupport.unpark(s.thread);
}123456789101112131415161718

ReentrantLock内存可见性分析

针对如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try {
    lock.lock();
    i ++;
} finally {
    lock.unlock();
}123456

可以发现哪怕在不使用 volatile关键字修饰元素i的时候, 这里的i 也是没有并发问题的。

互斥锁

保证在同一时刻只有一个线程对其进行操作。比如最常见的 synchronized。

参考文献

Java中的公平锁和非公平锁实现详解

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年06月21日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java多线程,对锁机制的进一步分析
可重入锁,也叫递归锁。它有两层含义,第一,当一个线程在外层函数得到可重入锁后,能直接递归地调用该函数,第二,同一线程在外层函数获得可重入锁后,内层函数可以直接获取该锁对应其它代码的控制权。之前我们提到的synchronized和ReentrantLock都是可重入锁。
用户1153489
2020/02/18
5080
【Java多线程-7】阅尽Java千般锁
根据对同步资源处理策略不同,锁在宏观上分为乐观锁与悲观锁,这只是概念上的一种称呼,Java中并没有具体的实现类叫做乐观锁或者悲观锁。
云深i不知处
2020/09/16
3840
史上最全 Java 中各种锁的介绍
在计算机科学中,锁(lock)或互斥(mutex)是一种同步机制,用于在有许多执行线程的环境中强制对资源的访问限制。锁旨在强制实施互斥排他、并发控制策略。 锁通常需要硬件支持才能有效实施。这种支持通常采取一个或多个原子指令的形式,如"test-and-set", "fetch-and-add" or "compare-and-swap"”。这些指令允许单个进程测试锁是否空闲,如果空闲,则通过单个原子操作获取锁。
java金融
2020/08/04
3860
我去了,一篇文章,看懂锁???
Lock和synchronized,是最常见的锁,都可以达到线程安全的目的,功能常见不同
Joseph_青椒
2023/08/28
2240
我去了,一篇文章,看懂锁???
java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁
公平锁: 是指多个线程按照申请锁的顺序来获取锁。 非公平锁: 是指在多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象。饥饿现象就是低优先级的线程可能一直拿不到锁,而一直处于等待状态。
程序猿川子
2024/11/11
3260
探索JAVA并发 - 可重入锁和不可重入锁
第二次调用lock后线程就阻塞了,线程开始等待持有锁的线程放手,然而是它是它就是它。
acupt
2019/08/26
3K0
聊聊Java中的那些锁
说到Java中锁的分类,有很多种。本文就来聊聊公平锁、非公平锁、可重入锁、独占锁、共享锁和自旋锁。
贪挽懒月
2019/05/14
3370
聊聊Java中的那些锁
锁机制-java面试
何为同步?JVM规范规定JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明,但是方法的同步同样可以使用这两个指令来实现。monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处, JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对
Spark学习技巧
2018/04/17
9250
锁机制-java面试
Java锁的分类_地锁怎么安装
  在笔者面试过程时,经常会被问到各种各样的锁,如乐观锁、读写锁等等,非常繁多,在此做一个总结。介绍的内容如下:
全栈程序员站长
2022/09/20
2520
[JDK] 多线程高并发探秘之“锁”
自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区。
架构探险之道
2019/07/25
6950
Java中15种锁的介绍
在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下:
Java团长
2019/04/25
3970
JUC 多线程知识杂集
synchronized是关键字,属于JVM层面,monitorenter(底层是通过monitor对象来完成,其实wait/notify等方法也依赖于monitor对象只有在同步块或者方法中才能调用wait/notify等方法)
万能青年
2019/08/30
3110
《Java-SE-第二十七章》之常见的锁策略
 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
用户10517932
2023/10/07
1890
《Java-SE-第二十七章》之常见的锁策略
重学 Java 基础之线程基础(三)
对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java中,synchronized关键字和Lock的实现类都是悲观锁。
啵啵肠
2023/11/28
1910
不懂什么是锁?看看这篇你就明白了
Java 中的锁有很多,可以按照不同的功能、种类进行分类,下面是我对 Java 中一些常用锁的分类,包括一些基本的概述
cxuan
2019/12/19
4420
不懂什么是锁?看看这篇你就明白了
码妞:Java的一堆锁是干嘛的?能锁住体重吗?
博客:https://juejin.im/user/5c629a3051882562191755d8
陈宇明
2020/12/16
6610
多线程应用 - 超详细的AQS详情
这篇主要是分析下AQS的原理,说实话挺难懂的。写文章的时候也难以下手。先解释下AQS是什么。
虞大大
2020/08/26
5160
多线程应用 - 超详细的AQS详情
java多线程学习(1)-锁的简介
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
Simon、hao
2018/07/01
5150
Java锁机制
上一篇简略说了一下Synchronized和Lock,今天就来说一下Java的锁机制。
Liusy
2020/09/01
7720
Java锁机制
Java 多线程 (Part2: Java线程 Lock锁)
悲观锁: 一定会出现多线程场景,先加锁,Synchronized 和 Lock 都是悲观锁 (适合 write多)
JiahuiZhu1998
2022/12/30
4600
相关推荐
Java多线程,对锁机制的进一步分析
更多 >
LV.1
广州趣丸网络科技有限公司JAVA开发工程师
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验