详解Java中的锁Lock和synchronized

一、Lock接口

1、Lock接口和synchronized内置锁

a)synchronized:Java提供的内置锁机制,Java中的每个对象都可以用作一个实现同步的锁(内置锁或者监视器Monitor),线程在进入同步代码块之前需要或者这把锁,在退出同步代码块会释放锁。而synchronized这种内置锁实际上是互斥的,即没把锁最多只能由一个线程持有。

b)Lock接口:Lock接口提供了与synchronized相似的同步功能,和synchronized(隐式的获取和释放锁,主要体现在线程进入同步代码块之前需要获取锁退出同步代码块需要释放锁)不同的是,Lock在使用的时候是显示的获取和释放锁。虽然Lock接口缺少了synchronized隐式获取释放锁的便捷性,但是对于锁的操作具有更强的可操作性、可控制性以及提供可中断操作和超时获取锁等机制。

2、lock接口使用的一般形式

Lock lock = new ReentrantLock(); //这里可以是自己实现Lock接口的实现类,也可以是jdk提供的同步组件
lock.lock();//一般不将锁的获取放在try语句块中,因为如果发生异常,在抛出异常的同时,也会导致锁的无故释放
try {
}finally {
    lock.unlock(); //放在finally代码块中,保证锁一定会被释放
}

3、Lock接口的方法

public interface Lock {

    /**
     * 获取锁,调用该方法的线程会获取锁,当获取到锁之后会从该方法但会
     */
    void lock();

    /**
     * 可响应中断。即在获取锁的过程中可以中断当前线程
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * 尝试非阻塞的获取锁,调用该方法之后会立即返回,如果获取到锁就返回true否则返回false
     */
    boolean tryLock();

    /**
     * 超时的获取锁,下面的三种情况会返回
     * ①当前线程在超时时间内获取到了锁
     * ②当前线程在超时时间内被中断
     * ③超时时间结束,返回false
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /**
     * 释放锁
     */
    void unlock();

    /**
     * 获取等待通知组件,该组件和当前锁绑定,当前线程只有获取到了锁才能调用组件的wait方法,调用该方法之后会释放锁
     */
    Condition newCondition();
}

4、相比于synchronized,Lock接口所具备的其他特性

①尝试非阻塞的获取锁tryLock():当前线程尝试获取锁,如果该时刻锁没有被其他线程获取到,就能成功获取并持有锁

②能被中断的获取锁lockInterruptibly():获取到锁的线程能够响应中断,当获取到锁的线程被中断的时候,会抛出中断异常同时释放持有的锁

③超时的获取锁tryLock(long time, TimeUnit unit):在指定的截止时间获取锁,如果没有获取到锁返回false

二、重入锁

1、重入锁的概念

当某个线程请求一个被其他线程所持有的锁的时候,该线程会被阻塞(后面的读写锁先不考虑在内),但是像synchronized这样的内置锁是可重入的,即一个线程试图获取一个已经被该线程所持有的锁,这个请求会成功。重入以为这锁的操作粒度是线程级别而不是调用级别。我们下面说到的ReentrantLock也是可重入的,而除了支持锁的重入之外,该同步组件也支持公平的和非公平的选择。

2、ReentrantLock

a)ReentrantLock实现的可重入性

对于锁的可重入性,需要解决的两个问题就是:

①线程再次获取锁的识别问题(锁需要识别当前要获取锁的线程是否为当前占有锁的线程);

②锁的释放(同一个线程多次获取同一把锁,那么锁的记录也会不同。一般来说,当同一个线程重复n次获取锁之后,只有在之后的释放n次锁之后,其他的线程才能去竞争这把锁)

③ReentrantLock的可重入测试

import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;

 public class TestCR {
     Lock lock = new ReentrantLock();

     void m1(){
         try{
             lock.lock(); // 加锁
             for(int i = 0; i < 4; i++){
                 TimeUnit.SECONDS.sleep(1);
                 System.out.println("m1() method " + i);
             }
             m2(); //在释放锁之前,调用m2方法
         }catch(InterruptedException e){
             e.printStackTrace();
         }finally{
             lock.unlock(); // 解锁
         }
     }

     void m2(){
         lock.lock();
         System.out.println("m2() method");
         lock.unlock();
     }

     public static void main(String[] args) {
         final TestCR t = new TestCR();
         new Thread(new Runnable() {
             @Override
             public void run() {
                 t.m1();
             }
         }).start();

         new Thread(new Runnable() {
             @Override
             public void run() {
                 t.m2();
             }
         }).start();
     }
 }

b)下面分析ReentrantLock的部分源码来学习这个同步组件(默认的非公平锁实现)

①首先可以知道ReentrantLock实现Lock接口public class ReentrantLock implements Lock

abstract static class Sync extends AbstractQueuedSynchronizer {
    /**
     * 创建非公平锁的方法
     */
    abstract void lock();

    /**
     * 执行非公平的tryLock。 tryAcquire实现于
     * 子类,但两者都需要tryf方法的非公平尝试。
     */
    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()) {//增加再次获取同步状态的处理逻辑
            int nextc = c + acquires; //如果再次尝试获取同步状态的线程就是当前已经占有同步状态的线程,那么就更新同步状态的值(进行增加操作)
            if (nextc < 0) // 对同步状态的值进行非法判断
                throw new Error("Maximum lock count exceeded");
            setState(nextc); //更新state的值
            return true;
        }
        return false;
    }

    /**
     * 释放同步状态的处理逻辑
     */
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases; //对同一线程而言,就是减去相应的获取次数
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false; //返回值
        if (c == 0) { //只有该线程将获取的次数全部释放之后,才会返回true,并且将当前同步状态的持有者设置为null
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c); //更新state
        return free;
    }

        /**
         * 判断当前同步状态的持有者线程
         */
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    final ConditionObject newCondition() {
        return new ConditionObject();
    }

        /**
         * 返回当前持有者线程
         */
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

        /**
         * 返回持有同步状态的线程获取次数
         */
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

        /**
         * 判断当前是否有线程获取到同步状态(根据state值进行判断)
         */
    final boolean isLocked() {
        return getState() != 0;
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

②通过上面的非公平锁的实现源码可以看到,ReentrantLock实现可重入的逻辑大概上是这样的:

获取逻辑:首先通过nonfairTryAcquire方法增加了对于同一线程再次获取同步状态的逻辑处理(通过判断当前线程是否为已经同步状态的持有者,来决定是否能够再次获取同步状态,如果当前线程是已经获取到同步状态的那个线程,那么就能够获取成功,并且同时以CAS的方式修改state的值)

释放逻辑:对于成功获取到同步状态的线程,在释放锁的时候,通过tryRelease方法的实现可以看出,如果该锁被线程获取到了n次,那么前(n-1)次释放的操作都会返回false,只有将同步状态完全释放才会返回true。最终获取到同步状态的线程在完全释放掉之后,state值为0并且持有锁的线程为null。

c)关于ReentrantLock的公平和非公平实现

①非公平锁

公平和非公平是针对于获取锁而言的,对于公平锁而言获取锁应该遵循FIFO原则,上面我们通过源码分析了非公平锁的实现(对于非公平锁而言,tryAcquire方法直接使用的是ReentrantLock静态内部类Sync的nofairTryAcquire方法)

//非公平锁实现
static final class NonfairSync extends Sync {

    /**
     * 以CAS方式原子的更新state的值
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    /**
     * 非公平锁的实现是直接调用Sync的nonfairTryAcquire方法
     */
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

②公平锁实现

公平锁的实现和非公平实现的主要区别就是tryAcquire方法的实现

static final class FairSync extends Sync {

    final void lock() {
        acquire(1); //调用AQS的模板方法实现锁的获取
    }

    /**
     * 公平锁的处理逻辑
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread(); //获取当前线程
        int c = getState(); //获取当前同步状态的值
        if (c == 0) { //当前同步状态没有被任何线程获取的时候
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) { //这个点的主要处理逻辑就是:hasQueuedPredecessors判断当前线程所在的结点是否含有前驱结点,                                  如果返回值为true表示有前驱结点,那么当前线程需要等待前驱结点中的线程获取并释放锁之后才能获取锁,保证了FIFO
                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;
    }
}
//hasQueuedPredecessors的处理逻辑
public final boolean hasQueuedPredecessors() {
    // 简单而言,就是判断当前线程是否有前驱结点
    // 当前结点含有前驱结点时候返回true;当前结点为头结点挥着队列为空的时候返回false
    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());
}

d)公平锁和非公平锁的测试

①测试目的

验证上面通过源码分析的,非公平锁在获取锁的时候会首先进行抢锁,在获取锁失败后才会将当前线程加入同步队列队尾中,而公平锁则是符合请求的绝对顺序,也就是会按照先来后到FIFO。在下面的代码中我们使用一个静态内部类继承了ReentrantLock并重写等待队列的方法,作为测试的ReentrantLock。然后创建5个线程,每个线程连续两次去获取锁,分别测试公平锁和非公平锁的测试结果

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.junit.Test;

public class TestReentrantLock {
    /**
     * ReentrantLock的构造方法
     * public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
     */
    private Lock fairLock = new ReentrantLock2(true);
    private Lock unFairLock = new ReentrantLock2(false);

    @Test
    public void testFair() throws InterruptedException {
        testLock(fairLock); //测试公平锁
    }

    @Test
    public void testUnFair() throws InterruptedException {
        testLock(unFairLock); //测试非公平锁
    }

    private void testLock(Lock lock) throws InterruptedException {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Job(lock)) {
                public String toString() {
                        return getName();
                }
            };
            thread.setName(i+"");
            thread.start();
        }
        Thread.sleep(12000);
    }

    private static class Job extends Thread {
        private Lock lock;
        public Job(Lock lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            //两次打印当前线程和等待队列中的Threads
            for (int i = 0; i < 2; i++) {
                lock.lock(); //获取锁
                try {
                    Thread.sleep(1000);
                    System.out.println("当前线程=>" + Thread.currentThread().getName() + " " +
                            "等待队列中的线程=>" + ((ReentrantLock2)lock).getQueuedThreads());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock(); //释放锁
                }
            }
        }

    }

    private static class ReentrantLock2 extends ReentrantLock {
        public ReentrantLock2(boolean fair) {
            super(fair);
        }
        public Collection<Thread> getQueuedThreads() { //逆序打印等待队列中的线程
            List<Thread> list = new ArrayList<Thread>(super.getQueuedThreads());
            Collections.reverse(list);
            return list;
        }
    }

}

②测试非公平锁

由上面的测试结果简单的得到关于非公平锁的一个结论:通过nofairTryAcquire方法可以得到这样一个前提,当一个线程请求一个锁时,判断获取成功的条件就是这个线程获取到同步状态就可以,那么某个刚刚释放锁的线程再次获取到同步状态的几率就会更大一些(当然实验中也出现并非连续两次获取这把锁的情况,比如下面的测试结果)

③测试公平锁

通过分析下面的测试结果,对于使用公平锁而言,即便是同一个线程连续两次获取锁释放锁,在第一次释放锁之后还是会被放在队尾并从队列头部拿出线程进行执行。并没有出现像非公平锁那样连续两次获取锁的那种情况

④由上面的测试可以看出:非公平锁可能导致在队尾的线程饥饿,但是又因为同一个线程在释放锁的时候有更大的概率再次获取到这把锁,那么这样的话线程的切换次数就会更少(这带来的就是更大的吞吐量和开销的减小)。而虽然公平锁的获取严格按照FIFO的规则,但是线程切换的次数就会更多。

三、Synchronized

1、Synchronized作用对象

①对于普通方法,锁的是当前实例对象

②对于静态同步方法,锁的是类的Class对象

③对于同步代码块,锁的是Synchronized括号中的对象

如下所示的三种情况

package cn.source.sync;

public class TestSync01 {
    private static int count = 0;
    private Object object = new Object();

    public void testSyn1() {
        //同步代码块(这里面是锁临界资源,即括号中的对象)
        synchronized (object) {
            System.out.println(Thread.currentThread().getName()
                +" count =" + count++);
        }
    }

    public void testSyn2() {
        //锁当前对象(相当于普通同步方法)
        synchronized (this) {
            System.out.println(Thread.currentThread().getName()
                    +" count =" + count++);
        }
    }

    //普通同步方法:锁当前对象
    public synchronized void testSyn3() {
        System.out.println(Thread.currentThread().getName()
                +" count =" + count++);
    }

    //静态同步方法,锁的是当前类型的类对象(即TestSync01.class)
    public static synchronized void testSyn4() {
        System.out.println(Thread.currentThread().getName()
                +" count =" + count++);
    }

    //下面的这种方式也是锁当前类型的类对象
    public static void testSyn5() {
        synchronized (TestSync01.class) {
            System.out.println(Thread.currentThread().getName()
                    +" count =" + count ++);
        }
    }
}

2、synchronized的实现原理

①Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现。同步代码块是使用monitorenter和monitorexit来实现的,同步方法 并不是由 monitor enter 和 monitor exit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。monitorenter指令是在编译后插入同步代码块的起始位置,而monitorexit指令是在方法结束处和异常处,每个对象都有一个monitor与之关联,当一个monitor被持有后它就会处于锁定状态。

②synchronized用的锁是存在Java对象头(非数组类型包括Mark Word、类型指针,数组类型多了数组长度)里面的,对象头中的Mark Word存储对象的hashCode,分代年龄和锁标记位,类型指针指向对象的元数据信息,JVM通过这个指针确定该对象是那个类的实例等信息。

③当在对象上加锁的时候,数据是记录在对象头中,对象头中的Mark Word里存储的数据会随着锁标志位的变化而变化(无锁、轻量级锁00、重量级锁10、偏向锁01)。当执行synchronized的同步方法或者同步代码块时候会在对象头中记录锁标记,锁标记指向的是monitor对象(也称为管程或者监视器锁)的起始地址。由于每个对象都有一个monitor与之关联,monitor和与关联的对象一起创建(当线程试图获取锁的时候)或销毁,当monitor被某个线程持有之后,就处于锁定状态。

④Hotspot虚拟机中的实现,通过ObjectMonitor来实现的

如图所示,ObjectMonitor中有两个队列(EntryList、WaitSet)以及锁持有者Owner标记,其中WaitSet是哪些调用wait方法之后被阻塞等待的线程队列,EntryList是ContentionList中能有资格获取锁的线程队列。当多个线程并发访问同一个同步代码时候,首先会进入EntryList,当线程获得锁之后monitor中的Owner标记会记录此线程,并在该monitor中的计数器执行递增计算代表当前锁被持有锁定,而没有获取到的线程继续在EntryList中阻塞等待。如果线程调用了wait方法,则monitor中的计数器执行赋0运算,并且将Owner标记赋值为null,代表当前没有线程持有锁,同时调用wait方法的线程进入WaitSet队列中阻塞等待,直到持有锁的执行线程调用notify/notifyAll方法唤醒WaitSet中的线程,唤醒的线程进入EntryList中等待锁的获取。除了使用wait方法可以将修改monitor的状态之外,显然持有锁的线程的同步代码块执行结束也会释放锁标记,monitor中的Owner会被赋值为null,计数器赋值为0。如下图所示

3、锁的种类、升级和对比

a)锁的种类

Java 中锁的种类大致分为偏向锁,自旋锁,轻量级锁,重量级锁。锁的使用方式为:先提供偏向锁,如果不满足的时候,升级为轻量级锁,再不满足,升级为重量级锁。自旋锁是一个过渡的锁状态,不是一种实际的锁类型。锁只能升级,不能降级。

b)锁的升级

①偏向锁

如果代码中基本不可能出现多线程并发争抢同一个锁的时候,JVM 编译代码,解释执行的时候,会自动的放弃同步信息,消除 synchronized 的同步代码结果,使用锁标记的形式记录锁状态。具体的实现方式大概就是:当一个线程访问同步块并获取锁的时候,会在对象头和栈帧的锁记录中存储偏向的线程ID,之后线程在进入和退出同步块的时候不需要使用CAS进行加锁和解锁,只需要测试对象头中的MarkWord中是否存储着当前线程的偏向锁;如果测试成功,就表示线程获取锁成功,如果测试失败需要检查对象头中的MarkWord的偏向锁表示是否设置为1,如果没有设置就使用CAS竞争锁,设置了就以CAS方式将偏向锁设置为当前线程。在 Monitor 中有变量 ACC_SYNCHRONIZED。当变量值使用的时候,代表偏向锁锁定。使用偏向锁可以避免锁的争抢和锁池状态的维护。提高效率。

②轻量级锁

当偏向锁不满足,也就是有多线程并发访问,锁定同一个对象的时候,先提升为轻量级锁。也是使用标记 ACC_SYNCHRONIZED 标记记录的。ACC_UNSYNCHRONIZED 标记记录未获取到锁信息的线程。就是只有两个线程争抢锁标记的时候,优先使用轻量级锁。(自旋锁)当获取锁的过程中,未获取到。为了提高效率,JVM 自动执行若干次空循环,再次申请锁,而不是进入阻塞状态的情况。称为自旋锁。自旋锁提高效率就是避免线程状态的变更

③重量级锁

在自旋过程中,为了避免无用的自旋(比如获得锁的线程被阻塞住了),锁就会被升级为重量级锁。在重量级锁的状态下,其他线程视图获取锁的时候都会被阻塞住,只有持有锁的线程释放锁之后才会唤醒那些阻塞的线程,这些线程就开始竞争锁。

4、关于synchronized的其他说明

a)关于同步方法和非同步方法

同步方法只影响 锁定同一个锁对象的同步方法,不影响非同步方法被其他线程调用,也不影响其他所资源的同步方法(简单理解就是锁的不是同一个资源,就不会影响);

b)synchronized是可重入的

同一个线程,多次调用同步代码,锁定同一个对象,是可重入的;

c)关于同步的继承问题

同一个线程中,子类同步方法覆盖父类的同步方法,可以指定调用父类的同步方法(相当于锁的重入)

d)锁与异常

当同步方法出现异常的时候会自动释放锁,不会影响其他线程的执行

e)synchronized锁的是对象,而不是引用

同步代码一旦加锁之后会有一个临时锁引用执行锁对象,和真实的引用无直接关联,在锁释放之前,修改锁对象引用不会影响同步代码块的执行

f)synchronized中的常量问题

在定义同步代码块的时候,不要使用常量对象作为锁对象

以上就是详解Java中的锁Lock和synchronized的详细内容,更多关于Java Lock synchronized的资料请关注我们其它相关文章!

时间: 2021-06-10

深入理解java内置锁(synchronized)和显式锁(ReentrantLock)

synchronized 和 Reentrantlock 多线程编程中,当代码需要同步时我们会用到锁.Java为我们提供了内置锁(synchronized)和显式锁(ReentrantLock)两种同步方式.显式锁是JDK1.5引入的,这两种锁有什么异同呢?是仅仅增加了一种选择还是另有其因?本文为您一探究竟. // synchronized关键字用法示例 public synchronized void add(int t){// 同步方法 this.v += t; } public stati

Java synchronized锁升级jol过程详解

jol(java object layout)需要的依赖 <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.10</version> </dependency> 一.synchronized锁对象的升级(膨胀)过程主要如下: 1.膨胀过程:无锁(锁对象初始化时)-> 偏向

史上最通俗理解的Java死锁代码演示

死锁的概念 知识储备 对象锁:Java一切皆对象,每个类都有一个class文件.由class文件可以new出对象,我们简单认识 下java对象,对象有个对象头信息,也就是这个对象概述,其中一条信息就是对象锁,也就是我们当前对象有没有被锁定,被哪个引用锁定. synchronized:synchronized是java关键词,如果运用到方法上代表我们锁的是这个方法,如果我们锁的代码块,代表再这个代码块内我们持有这个锁,Java Effective也是提倡减小锁的范围.我们进入同步代码块会加锁,执行

Java可重入锁的实现原理与应用场景

可重入锁,从字面来理解,就是可以重复进入的锁. 可重入锁,也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响. 在JAVA环境下ReentrantLock和synchronized都是可重入锁. synchronized是一个可重入锁.在一个类中,如果synchronized方法1调用了synchronized方法2,方法2是可以正常执行的,这说明synchronized是可重入锁.否则,在执行方法2想获取锁的时候,该锁已经在执行方法1时获取了,那么方法

JVM---jstack分析Java线程CPU占用,线程死锁的解决

本文章主要演示在Windows环境,Linux环境也差不多. 一.分析CPU占用飙高 首先写一个Java程序,并模拟一个死循环.让CPU使用率飙高.CPU负载过大的话,新的请求就处理不了了,这就是很多程序变慢了甚至不能访问的原因之一. 下面是我这里的Controller,启动程序之后,开多个请求访问这个方法.死循环代码就不贴了,自己构造.我这里模拟的一个截取字符串的死循环. /** * 演示死循环导致cpu使用率飙高 * */ @RequestMapping("/loop") publ

透彻理解Java中Synchronized(对象锁)和Static Synchronized(类锁)的区别

本文讲述了Java中Synchronized(对象锁)和Static Synchronized(类锁)的区别.分享给大家供大家参考,具体如下: Synchronized和Static Synchronized区别 通过分析这两个用法的分析,我们可以理解java中锁的概念.一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线程都共享该锁).实例锁对应的就是synchronized关键字,而类锁(全局锁)对应的就是

Java注解如何基于Redission实现分布式锁

这篇文章主要介绍了Java注解如何基于Redission实现分布式锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.定义注解类 @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DistributedLock { //锁名称 String lockName() default ""; /

Java redisson实现分布式锁原理详解

Redisson分布式锁 之前的基于注解的锁有一种锁是基本redis的分布式锁,锁的实现我是基于redisson组件提供的RLock,这篇来看看redisson是如何实现锁的. 不同版本实现锁的机制并不相同 引用的redisson最近发布的版本3.2.3,不同的版本可能实现锁的机制并不相同,早期版本好像是采用简单的setnx,getset等常规命令来配置完成,而后期由于redis支持了脚本Lua变更了实现原理. <dependency> <groupId>org.redisson&

Java Redis分布式锁的正确实现方式详解

前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性.在任意时刻,只有一个客户端能持有锁. 不会发生死锁.即使有一个客户端在

Java锁升级的实现过程

对象内存布局 Java对象在内存中存储的布局可以分为3块区域: 对象头.实例数据.对齐填充. 对象头,分为两个部分,第一个部分存储对象自身的运行时数据,又称为Mark Word,32位虚拟机占32bit,64位虚拟机占64bit.如图所示,不同锁状态下,Mark Word的结构,理解下面要介绍的各种锁,和锁升级过程,都需要先充分了解Mark Word的结构. 第二部分是类型指针,指向类元数据指针,虚拟机通过此指针,确定该对象属于那个类的实例. 轻量级锁 轻量级锁是相对于重量级锁(Synchrno

Java锁的升级策略 偏向锁 轻量级锁 重量级锁

这三种锁是指锁的状态,并且是专门针对Synchronized关键字.JDK 1.6 为了减少"重量级锁"的性能消耗,引入了"偏向锁"和"轻量级锁",锁一共拥有4种状态:无锁状态.偏向锁.轻量级锁.重量级锁.锁状态是通过对象头的Mark Word来进行标记的: 锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁,这种锁升级却不能降级的策略,是为了提高获得锁和释放锁的效率 重量级锁:依赖于底层操作系统的Mutex Lock,线程会被阻

SQL Server误区30日谈 第23天 有关锁升级的误区

误区 #23: 锁升级的过程是由行锁升级到页锁,再由页锁升级到表锁错误    实际不是,在SQL Server 2005和之前的版本,页锁会直接升级到表锁.    在SQL Server 2005或SQL Server 2008,你可以通过如下跟踪标志改变锁升级的行为: 标志1211-完全禁止锁升级,但锁使用的内存会被限制在动态分配内存的60%,当超过这个值时,更多的锁将会伴随着内存溢出错误而失败. 标志1224-禁止锁升级,但内存使用超过40%时,会自动开启锁升级    如果标志1211和12

Java 锁粗化与循环问题

1. 写在前面 "JVM 解剖公园"是一个持续更新的系列迷你博客,阅读每篇文章一般需要5到10分钟.限于篇幅,仅对某个主题按照问题.测试.基准程序.观察结果深入讲解.因此,这里的数据和讨论可以当轶事看,并没有做一致性.写作风格.句法和语义错误.重复或一致性检查.如果选择采信文中内容,风险自负. Aleksey Shipilёv,JVM 性能极客 推特 @shipilev 问题.评论.建议发送到 aleksey@shipilev.net 译注:锁粗化(Lock Coarsening).锁

浅谈Java垃圾回收的实现过程

本教程是为了理解基本的Java垃圾回收以及它是如何工作的.这是垃圾回收教程系列的第二部分.希望你已经读过了第一部分:<简单介绍Java垃圾回收机制>. Java垃圾回收是一项自动化的过程,用来管理程序所使用的运行时内存.通过这一自动化过程,JVM解除了程序员在程序中分配和释放内存资源的开销. 启动Java垃圾回收 作为一个自动的过程,程序员不需要在代码中显示地启动垃圾回收过程.System.gc()和Runtime.gc()用来请求JVM启动垃圾回收. 虽然这个请求机制提供给程序员一个启动GC

java中处理socket通信过程中粘包的情况

这两天学习了java中处理socket通信过程中粘包的情况,而且很重要,所以,今天添加一点小笔记. 处理粘包程序是客户端的接受消息线程: 客户端: import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Reader; import java.net.Socket; impo

详解Java线程池的增长过程

通过ThreadPoolExecutor的方式创建线程池 ThreadPoolExecutor 构造方法: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handle

通过Java读取xml文件内容过程解析

这篇文章主要介绍了通过Java读取xml文件内容过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 需要下载jar包dom4j:https://dom4j.github.io/ package com.zyb.xml; import java.io.File; import java.util.Iterator; import org.dom4j.Attribute; import org.dom4j.Document; import or

Java静态内部类实现单例过程

这篇文章主要介绍了Java静态内部类实现单例过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 枚举实现单例 线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用 public enum SingletonFactory { //枚举元素本身就是单例 INSTANCE; //添加自己需要的操作 public SingletonObject getInstance(){ return new SingletonObject();