Java中ReentrantLock和ReentrantReadWriteLock的原理

目录
  • ReentrantLock 原理
    • 概念
    • 核心变量和构造器
    • 核心方法
  • ReentrantReadWriteLock 原理
    • 用例
    • 核心变量和构造器
    • Sync类
    • tryAcquire获取写锁的流程
    • tryAcquireShared获取读锁的流程获取写锁的流程
    • fullTryAcquireShared完全获取读锁流程
    • tryRelease释放写锁的流程
    • tryReleaseShared释放读锁的流程
    • readerShouldBlock和writerShouldBlock模板方法公平锁实现
    • readerShouldBlock和writerShouldBlock模板方法非公平锁实现

ReentrantLock 原理

概念

基于AQS实现的可重入锁实现类。

核心变量和构造器

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;
    public ReentrantLock() {
        // 默认为非公平锁。为何默认为非公平锁?因为通过大量测试下来,发现非公平锁的性能优于公平锁
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        // 由fair变量来表明选择锁类型
        sync = fair ? new FairSync() : new NonfairSync();
    }
    abstract static class Sync extends AbstractQueuedSynchronizer {
        abstract void lock();
        // 非公平锁标准获取锁方法
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 当执行到这里时,正好获取所得线程释放了锁,那么可以尝试抢锁
            if (c == 0) {
                // 继续抢锁,不看有没有线程排队
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 当前线程就是持有锁的线程,表明锁重入
            else if (current == getExclusiveOwnerThread()) {
                // 利用state整形变量进行次数记录
                int nextc = c + acquires;
                // 如果超过了int表示范围,表明符号溢出,所以抛出异常0111 1111 + 1 = 1000 0000
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            // 返回false 表明需要AQS来将当前线程放入阻塞队列,然后进行阻塞操作等待唤醒获取锁
            return false;
        }
        // 公平锁和非公平锁公用方法,因为在释放锁的时候,并不区分是否公平
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            // 如果当前线程不是上锁的那个线程
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 不是重入锁,那么当前线程一定是释放锁了,然后我们把当前AQS用于保存当前锁对象的变量ExclusiveOwnerThread设置为null,表明释放锁成功
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            // 注意:此时state全局变量没有改变,也就意味着在setState之前,没有别的线程能够获取锁,这时保证了以上的操作原子性
            setState(c);
            // 告诉AQS,我当前释放锁成功了,你可以去唤醒正在等待锁的线程了
            return free;
        }
​
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
​
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
​
    }
​
    static final class NonfairSync extends Sync {
        // 由ReentrantLock调用获取锁
        final void lock() {
            // 非公平锁,直接抢锁,不管有没有线程排队
            if (compareAndSetState(0, 1))
                // 上锁成功,那么标识当前线程为获取锁的线程
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 抢锁失败,进入AQS的标准获取锁流程
                acquire(1);
        }
​
        protected final boolean tryAcquire(int acquires) {
            // 使用父类提供的获取非公平锁的方法来获取锁
            return nonfairTryAcquire(acquires);
        }
    }​
    static final class FairSync extends Sync {
        // 由ReentrantLock调用
        final void lock() {
            // 没有尝试抢锁,直接进入AQS标准获取锁流程
            acquire(1);
        }
        // AQS调用,子类自己实现获取锁的流程
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 此时有可能正好获取锁的线程释放了锁,也有可能本身就没有线程获取锁
            if (c == 0) {
                // 注意:这里和非公平锁的区别在于:hasQueuedPredecessors看看队列中是否有线程正在排队,没有的话再通过CAS抢锁
                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;
            }
            // 返回false 表明需要AQS来将当前线程放入阻塞队列,然后进行阻塞操作等待唤醒获取锁
            return false;
        }
    }
}

核心方法

获取锁操作:

public void lock() {
    // 直接通过sync同步器上锁
    sync.lock();
}

释放锁操作:

public void unlock() {
    sync.release(1);
}

ReentrantReadWriteLock 原理

用例

将原来的锁,分割为两把锁:读锁、写锁。适用于读多写少的场景,读锁可以并发,写锁与其他锁互斥。写写互斥、写读互斥、读读兼容。

public class ThreadDemo {
    static volatile int a;​
    public static void readA() {
        System.out.println(a);
    }​
    public static void writeA() {
        a++;
    }
    public static void main(String[] args) {
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        Thread readThread1 = new Thread(() -> {
            readLock.lock();
            try {
                readA();
            } finally {
                readLock.unlock();
            }
​
        });
        Thread readThread2 = new Thread(() -> {
            readLock.lock();
            try {
                readA();
            } finally {
                readLock.unlock();
            }
        });
​
        Thread writeThread = new Thread(() -> {
            writeLock.lock();
            try {
                writeA();
            } finally {
                writeLock.unlock();
            }
        });
​
        readThread1.start();
        readThread2.start();
        writeThread.start();
    }
}

核心变量和构造器

该接口用于获取读锁和写锁对象

public interface ReadWriteLock {
    // 用于获取读锁
    Lock readLock();
    // 用于获取写锁
    Lock writeLock();
}

readerLock和writerLock变量用于支撑以上描述的ReadWriteLock接口的读锁和写锁方法。通过构造方法得知,读写锁对象的创建和用例均依赖于公平锁或者非公平锁同步器。

public class ReentrantReadWriteLock implements ReadWriteLock {
    // 读锁对象
    private final ReentrantReadWriteLock.ReadLock readerLock;
    // 写锁对象
    private final ReentrantReadWriteLock.WriteLock writerLock;
    // 同步器
    final Sync sync;
    // 默认构造器,创建了非公平锁
    public ReentrantReadWriteLock() {
        this(false);
    }
    // 根据fair变量,来选择创建不同的锁:公平锁 FairSync 和非公平锁 NonfairSync
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        // 用同步器来创建读写锁对象
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }    
}

Sync类

核心变量和构造器

我们说读锁可以多个线程同时持有,而写锁只允许一个线程持有,此时我们称 读锁-----共享锁 写锁------互斥锁(排他锁)。然后我们在AQS中了解到一个变量state,它是32位的值,那么我们这里将其切割为高16位和低16位。

abstract static class Sync extends AbstractQueuedSynchronizer {
    // 高16位用于表示读锁
    static final int SHARED_SHIFT   = 16;
    // 用于对高16位操作:加1 减1
    static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
    // 最大读锁量
    static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
    // 用于获取低16位的值。例如 获取低八位:0000 0000 1111 1111
    static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
​
    /** 获取当前持有读锁的线程数量  */
    static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
    /** 获取当前持有写锁的线程数量 */
    static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
​
    // 高16位为所有读锁获取,那么我想知道每个线程对于读锁重入的次数?采用ThreadLocal来进行统计,每个线程自己统计自己的
    static final class HoldCounter {
        int count = 0;
        final long tid = getThreadId(Thread.currentThread());
    }
    // 继承自ThreadLocal,重写了其中的initialValue方法,该方法将在线程第一次获取该变量时调用初始化HoldCounter计数器
    static final class ThreadLocalHoldCounter
        extends ThreadLocal<HoldCounter> {
        public HoldCounter initialValue() {
            return new HoldCounter();
        }
    }
    // 创建ThreadLocal对象
    private transient ThreadLocalHoldCounter readHolds;
    // 缓存最后一个线程获取的读锁数量
    private transient HoldCounter cachedHoldCounter;
    // 保存获取到该锁的第一个读锁线程
    private transient Thread firstReader = null;
    // 保存第一个该锁的第一个读锁线程获取到的读锁数量
    private transient int firstReaderHoldCount;
​
    Sync() {
        // 构造器中初始化ThreadLocalHoldCounter ThreadLocal对象
        readHolds = new ThreadLocalHoldCounter();
        // 用于保证可见性,使用了state变量的volatile语义
        setState(getState());
    }
}

tryAcquire获取写锁的流程

由AQS调用,用于子类实现自己的上锁逻辑,和原有获取互斥锁保持一致,

protected final boolean tryAcquire(int acquires) {
    // 获取当前线程
    Thread current = Thread.currentThread();
    // 获取当前状态值和互斥锁的数量
    int c = getState();
    int w = exclusiveCount(c);
    // 状态值有效
    if (c != 0) {
        // 有线程获取到了读锁或者当前线程不是持有互斥锁的线程
        if (w == 0 ||  // 有线程获取到了读锁
            current != getExclusiveOwnerThread()) // 有线程获取到了写锁
            // 返回false 让AQS执行阻塞操作
            return false;
        // 写锁重入,而又由于写锁的数量保存在低16位,所以直接加就行了
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        setState(c + acquires);
        return true;
    }
    // 既没有读锁,也没有写锁
    if (writerShouldBlock() || // 由子类实现判断当前线程是否应该获取写锁
        !compareAndSetState(c, c + acquires)) // 通过CAS抢写锁
        return false;
    // 获取写锁成功,那么将当前线程标识为获取互斥锁的线程对象
    setExclusiveOwnerThread(current);
    return true;
}

tryAcquireShared获取读锁的流程获取写锁的流程

protected final int tryAcquireShared(int unused) {
    // 获取到当前线程对象
    Thread current = Thread.currentThread();
    // 获取到当前状态值
    int c = getState();
    if (exclusiveCount(c) != 0 && // 有没有线程持有写锁
        getExclusiveOwnerThread() != current) // 如果有线程获取到了互斥锁,那么进一步看看是不是当前线程
        // 不是当前线程,那么直接返回-1,告诉AQS获取共享锁失败
        return -1;
    // 获取到读锁的持有数量
    int r = sharedCount(c);
    if (!readerShouldBlock() && // 让子类来判定当前获取读锁的线程是否应该被阻塞
        r < MAX_COUNT && // 判断是否发生了溢出
        compareAndSetState(c, c + SHARED_UNIT)) { // 直接CAS 增加state的高16位的读锁持有数量
        // 增加高16位之前的计数为0,此时表明当前线程就是第一个获取读锁的线程
        if (r == 0) {
            // 注意:持有两个变量来优化threadlocal
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            // 当前获取读锁的线程就是一个线程,那么此时表明:锁重入,直接++计数位即可
            firstReaderHoldCount++;
        } else {
            // 当前线程不是第一个读线程,此时将其获取读锁的次数保存在ThreadLocal中
            HoldCounter rh = cachedHoldCounter;
            if (rh == null || rh.tid != getThreadId(current))
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }
    // 有很多同学走到这里,直接懵逼?不知道这是啥情况?经验:在看doug lea写的代码时,请注意:经常做优化,就是把一些常见的场景前置,保证性能
    return fullTryAcquireShared(current);
}

fullTryAcquireShared完全获取读锁流程

final int fullTryAcquireShared(Thread current) {
    HoldCounter rh = null;
    for (;;) {
        int c = getState();
        // 当前已经有线程获取到写锁且当前获取写锁的线程不是,当前线程
        if (exclusiveCount(c) != 0) {
            if (getExclusiveOwnerThread() != current)
                return -1;
        } else if (readerShouldBlock()) {
            // 子类判断当前线程应该阻塞
            if (firstReader == current) {
                // 当前线程就是第一个获取到读锁的线程
            } else {
                // 获取到当前线程记录读锁重入次数的HoldCounter对象
                if (rh == null) {
                    rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current)) {
                        rh = readHolds.get();
                        if (rh.count == 0)
                            readHolds.remove();
                    }
                }
                // 当前读锁重入次数为0时,表明没有获取读锁,此时返回-1,阻塞当前线程
                if (rh.count == 0)
                    return -1;
            }
        }
        // 读锁获取次数溢出
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // CAS增加读锁次数
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            if (sharedCount(c) == 0) {
                firstReader = current;
                firstReaderHoldCount = 1;
            } else if (firstReader == current) {
                firstReaderHoldCount++;
            } else {
                if (rh == null)
                    rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                else if (rh.count == 0)
                    readHolds.set(rh);
                rh.count++;
                cachedHoldCounter = rh;
            }
            return 1;
        }
    }
}

tryRelease释放写锁的流程

protected final boolean tryRelease(int releases) {
    // 没有获取写锁,为啥能释放写锁呢?
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    int nextc = getState() - releases;
    // 释放完毕后,写锁状态是否为0(锁重入),因为此时计算的不是当前state,是nextc
    boolean free = exclusiveCount(nextc) == 0;
    // 如果下一个状态值为0,此时表明当前线程完全释放了锁,也即锁重入为0,那么将当前线程对象从OwnerThread中移除
    if (free)
        setExclusiveOwnerThread(null);
    // 此时设置全局state变量即可
    setState(nextc);
    // 如果返回为true,那么由AQS完成后面线程的唤醒
    return free;
}

tryReleaseShared释放读锁的流程

释放时,需要考虑:重入多少次,就释放多少次。总结:先完成自己的释放,然后再完成共享的高16位的释放。

protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    // 当前线程是第一个获取到读锁的线程
    if (firstReader == current) {
        // 当前重入次数为1,代表什么?代表可以直接释放,如果不是1,那么表明还持有多个读锁,也即重入多次,那么直接--
        if (firstReaderHoldCount == 1)
            firstReader = null;
        else
            firstReaderHoldCount--;
    } else {
        HoldCounter rh = cachedHoldCounter;
        if (rh == null || rh.tid != getThreadId(current))
            rh = readHolds.get();
        int count = rh.count;
        if (count <= 1) {
            // 当前线程已经释放完读锁,那么不需要在ThreadLocal里持有HoldCounter对象
            readHolds.remove();
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        --rh.count;
    }
    for (;;) {
        // CAS释放高16位计数
        int c = getState();
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc))
            // 释放完毕后是否为0,为无锁状态,此时需要干啥?由AQS来唤醒阻塞的线程
            return nextc == 0;
    }
}

readerShouldBlock和writerShouldBlock模板方法公平锁实现

判断条件只有一个:hasQueuedPredecessors()方法,就是看看AQS的阻塞队列里是否有其他线程正在等待,如果有排队去。

总结:有人在排队,那么不插队。w->r->r->r 此时来了个r:w->r->r->r->r, 此时来了个w:w->r->r->r->w。

static final class FairSync extends Sync {
    final boolean writerShouldBlock() {
        return hasQueuedPredecessors();
    }
    final boolean readerShouldBlock() {
        return hasQueuedPredecessors();
    } // w->r->r   r获取锁  w->r->r-r
}

readerShouldBlock和writerShouldBlock模板方法非公平锁实现

写线程永远false,因为读写锁本身适用的是读多写少,此时不应该 让写线程饥饿,而且非公平,写锁永远不阻塞,让它抢,不管前面是否有人排队,先抢了再说。apparentlyFirstQueuedIsExclusive()第一个排队的是不是写线程。r(10),当前线程是第十一个,此时已经有一个写线程排队,r(10)->w,此时排队去。r(10)->w->r。

static final class NonfairSync extends Sync {
    final boolean writerShouldBlock() {
        return false;
    }
    final boolean readerShouldBlock() {
        return apparentlyFirstQueuedIsExclusive();
    } // w->r->r   r获取锁  r->r->r
}

到此这篇关于Java中ReentrantLock和ReentrantReadWriteLock的原理的文章就介绍到这了,更多相关Java ReentrantLock内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java多线程读写锁ReentrantReadWriteLock类详解

    目录 ReentrantReadWriteLock 读读共享 写写互斥 读写互斥 源码分析 写锁的获取与释放 读锁的获取与释放 参考文献 真实的多线程业务开发中,最常用到的逻辑就是数据的读写,ReentrantLock虽然具有完全互斥排他的效果(即同一时间只有一个线程正在执行lock后面的任务),这样做虽然保证了实例变量的线程安全性,但效率却是非常低下的.所以在JDK中提供了一种读写锁ReentrantReadWriteLock类,使用它可以加快运行效率. 读写锁表示两个锁,一个是读操作相关的锁

  • Java多线程 ReentrantReadWriteLock原理及实例详解

    读写锁ReentrantReadWriteLock概述 读写锁ReentrantReadWriteLock,使用它比ReentrantLock效率更高. 读写锁表示两个锁,一个是读操作相关的锁,称为共享锁:另一个是写操作相关的锁,称为排他锁. 1.读和读之间不互斥,因为读操作不会有线程安全问题 2.写和写之间互斥,避免一个写操作影响另外一个写操作,引发线程安全问题 3.读和写之间互斥,避免读操作的时候写操作修改了内容,引发线程安全问题 多个Thread可以同时进行读取操作,但是同一时刻只允许一个

  • Java并发编程之浅谈ReentrantLock

    一.首先看图 二.lock()跟踪源码 这里对公平锁和非公平锁做了不同实现,由构造方法参数决定是否公平. public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } 2.1 非公平锁实现 static final class NonfairSync extends Sync { private static final long serialVersionUID = 731615

  • Java常用锁synchronized和ReentrantLock的区别

    目录 区别1:用法不同 synchronized 基础使用 ReentrantLock 基础使用 区别2:获取锁和释放锁方式不同 区别3:锁类型不同 区别4:响应中断不同 区别5:底层实现不同 小结 前言: 在 Java 中,常用的锁有两种:synchronized(内置锁)和 ReentrantLock(可重入锁),二者的功效都是相同得,但又有很多不同点,所以我们今天就来聊聊. 区别1:用法不同 synchronized 可用来修饰普通方法.静态方法和代码块,而 ReentrantLock 只

  • Java多线程之ReentrantReadWriteLock源码解析

    一.介绍 1.1 ReentrantReadWriteLock ReentrantReadWriteLock 是一个读写锁,允许多个读或者一个写线程在执行. 内部的 Sync 继承自 AQS,这个 Sync 包含一个共享读锁 ReadLock 和一个独占写锁 WriteLock. 该锁可以设置公平和非公平,默认非公平. 一个持有写锁的线程可以获取读锁.如果该线程先持有写锁,再持有读锁并释放写锁,称为锁降级. WriteLock支持Condition并且与ReentrantLock语义一致,而Re

  • Java 多线程并发 ReentrantReadWriteLock详情

    目录 前言 ReadWriteLock ReentrantReadWriteLock 源码分析 类关系 Sync HoldCounter ThreadLocalHoldCounter 属性 构造方法 核心方法 锁的计数方法 读写锁阻塞检查方法 公平策略实现 FairSync 和非公平策略实现 NonfairSync NonfairSync 非公平策略 FairSync 公平策略 Release 和 Acquire 方法组 ReadLock WriteLock 读写锁降级 总结 前言 Reentr

  • 彻底了解java中ReentrantLock和AQS的源码

    一.前言 首先在聊ReentrantLock之前,我们需要知道整个JUC的并发同步的基石,currrent里面所有的共享变量都是由volatile修饰的,我们知道volatile的语义有2大特点,可见性以及防止重排序(内存屏障,volatie写与volatile读) 1.当第二个操作为volatile写操做时,不管第一个操作是什么(普通读写或者volatile读写),都不能进行重排序.这个规则确保volatile写之前的所有操作都不会被重排序到volatile之后; 2.当第一个操作为volat

  • Java如何使用ReentrantLock实现长轮询

    Java代码 1. ReentrantLock 加锁阻塞,一个condition对应一个线程,以便于唤醒时使用该condition一定会唤醒该线程 /** * 获取探测点数据,长轮询实现 * @param messageId * @return */ public JSONObject getToutData(String messageId) { Message message = toutMessageCache.get(messageId); if (message == null) {

  • Java concurrency之共享锁和ReentrantReadWriteLock_动力节点Java学院整理

    ReadWriteLock 和 ReentrantReadWriteLock介绍 ReadWriteLock,顾名思义,是读写锁.它维护了一对相关的锁 - - "读取锁"和"写入锁",一个用于读取操作,另一个用于写入操作. "读取锁"用于只读操作,它是"共享锁",能同时被多个线程获取. "写入锁"用于写入操作,它是"独占锁",写入锁只能被一个线程锁获取. 注意:不能同时存在读取锁和写入锁

  • Java中ReentrantLock和ReentrantReadWriteLock的原理

    目录 ReentrantLock 原理 概念 核心变量和构造器 核心方法 ReentrantReadWriteLock 原理 用例 核心变量和构造器 Sync类 tryAcquire获取写锁的流程 tryAcquireShared获取读锁的流程获取写锁的流程 fullTryAcquireShared完全获取读锁流程 tryRelease释放写锁的流程 tryReleaseShared释放读锁的流程 readerShouldBlock和writerShouldBlock模板方法公平锁实现 read

  • Java中读写锁ReadWriteLock的原理与应用详解

    目录 什么是读写锁? 为什么需要读写锁? 读写锁的特点 读写锁的使用场景 读写锁的主要成员和结构图 读写锁的实现原理 读写锁总结 Java并发编程提供了读写锁,主要用于读多写少的场景,今天我就重点来讲解读写锁的底层实现原理 什么是读写锁? 读写锁并不是JAVA所特有的读写锁(Readers-Writer Lock)顾名思义是一把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,因为读操作本身是线程安全的,而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的. 所谓的读

  • Java中ExecutorService和ThreadPoolExecutor运行原理

    目录 为什么要使用线程池 线程池的创建 线程的提交方法 具体实现 总结1 ThreadPoolExecutor运行原理 总结2 为什么要使用线程池 服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的. 构建服务器应用程序的一个过于简单的模型应该是:每当一个请求到达就创建一个新线程,然后在新线程中为请求服务.实际上,对于原型开发这种方法工作得很好,但如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显. 每个请求对应一个线程(thread-per-

  • 一文带你搞懂Java中Synchronized和Lock的原理与使用

    目录 1.Synchronized与Lock对比 2.Synchronized与Lock原理 2.1 Synchronized原理 2.2 Lock原理 3.Synchronized与Lock使用 Synchronized Lock 4.相关问题 1.Synchronized与Lock对比 实现方式:Synchronized是Java语言内置的关键字,而Lock是一个Java接口. 锁的获取和释放:Synchronized是隐式获取和释放锁,由Java虚拟机自动完成:而Lock需要显式地调用lo

  • 浅谈Java中的atomic包实现原理及应用

    1.同步问题的提出 假设我们使用一个双核处理器执行A和B两个线程,核1执行A线程,而核2执行B线程,这两个线程现在都要对名为obj的对象的成员变量i进行加1操作,假设i的初始值为0,理论上两个线程运行后i的值应该变成2,但实际上很有可能结果为1. 我们现在来分析原因,这里为了分析的简单,我们不考虑缓存的情况,实际上有缓存会使结果为1的可能性增大.A线程将内存中的变量i读取到核1算数运算单元中,然后进行加1操作,再将这个计算结果写回到内存中,因为上述操作不是原子操作,只要B线程在A线程将i增加1的

  • java中Servlet监听器的工作原理及示例详解

    监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行. 监听器原理 监听原理 1.存在事件源 2.提供监听器 3.为事件源注册监听器 4.操作事件源,产生事件对象,将事件对象传递给监听器,并且执行监听器相应监听方法 监听器典型案例:监听window窗口的事件监听器 例如:swing开发首先制造Frame**窗体**,窗体本身也是一个显示空间,对窗体提供监听器,监听窗体方法调用或者属性改变:

  • Java中的迭代器和foreach原理

    迭代器是一种设计模式,它的定义为:提供一种方法访问一个容器对象中的各个元素,而又不需暴露该容器对象的内部细节.迭代器模式,就是为容器而生. 在Java中,Iterator称为迭代器,主要用于遍历 Collection 集合中的元素.Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力.如果需要创建Iterator 对象,则必须有一个被迭代的集合.Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了C

  • Java中OAuth2.0第三方授权原理与实战

    目录 RFC6749 OAuth 2.0授权4大模式 合同到期后的续约机制 OAuth2.0第三方授权实战 oauth-client oauth-server RFC6749 OAuth2的官方文档在RFC6749:https://datatracker.ietf.org/doc/html/rfc6749 以王者荣耀请求微信登录的过程为例 A:Client申请访问用户资源 B:用户授权(过程较复杂)一次有效 C:Client向Server请求一个长时间有效的token D:返回token E:使

  • 剖析Java中阻塞队列的实现原理及应用场景

    我们平时使用的一些常见队列都是非阻塞队列,比如PriorityQueue.LinkedList(LinkedList是双向链表,它实现了Dequeue接口). 使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦.但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素.当队列中有元素后,被阻塞的线程会自动

随机推荐