精华内容
下载资源
问答
  • JAVA锁

    2019-09-17 15:49:30
    一. JAVA的概念 1、自旋: 2、乐观: 3、悲观: 4、独享: 5:共享:(限流)

    一. JAVA中锁的概念

    1、自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其他的线程获取,那么该线程将循环等待,然后不断的判断是否能够被成功获取,知道获取到锁才会退出循环
    2、乐观锁:假定没有冲突,在修改数据的时候如果发现和之前的获取的不一致,则读取最新的数据,修改后重试修改
    3、悲观锁:界定会发生并发冲突,同步所有对数据的相关操作,从读数据就开始上锁
    4、独享锁:给资源加上写锁,线程可以修改资源,其他线程不能再加锁(单写)
    5:共享锁:(限流)给资源加上读锁之后只能读不能改,其他的线程也只能加读锁,不能加写锁(多读)
    6、可重入锁、不可重入锁:线程拿到一把锁之后,可以自由进入同一把锁所同步的其他代码

    二、Synchronized详解

    首先说下对象在堆中存的是怎么存的?

    1、对象值:最本质的就是对象在堆里面存了对象的值,类对象和一些方法的作为类的结构信息会存在方法区里面;如果类里面存在对象,那么只能存对象的引用。
    2、对象头:里面有引用会指向类对象,通过这个标记,会记录对象的值是属于哪个类。
    3、padding:对象存在堆中的规律是8个字节的整数倍,padding做补位,无太大的意义。

    重点在对象头部:
    在这里插入图片描述
    根据图片我们知道,对象头里面存了3个字段
    Mark Word:锁的状态
    Class Mate address:存储对象的引用,根据这个引用找到方法区的类对象。
    Array length:如果当前对象是个数组对象,这个字段则标记数组的长度。如果不是数组,这个字段无意义。

    Synchronized的加锁过程如下:

    要点在于Mark word:

    1、线程会在虚拟机栈内开辟一块内存区域 叫做Lock Record
    2、线程会把对象头里面的Mark Word里面的HashcodeAge0(无锁状态)写到这个Lock Record的内存区域
    3、多个线程枪锁的时候就会进行CAS操作(新值:Lock Record Adress;旧值:HashcodeAge0),抢到锁的线程会把HashcodeAge0值替换成Lock Record Addess值,这个时候说明一个线程枪锁成功,没有抢到锁的线程会发生自旋,当自旋到达一定的次数(具体几次,暂时没有查到可靠的资料)之后,或者其他线程来了之后CAS的时候compare没有成功,这两种原因都会造成Mark Word里面的锁升级为重量级锁Monitor address。

    对象监视器Monitor:
    JVM会帮我们维护一个特殊机制(对象监视器),java中每一个对象都可能存在一个对象监视器,这个监视器来监视我们的对象,最大的作用就是实现锁的机制,这个Monitor里面有一个owner,这个owner相当对一个线程的引用,它会记录是哪个线程抢到了锁,类似于owner=thread1。
    未抢到锁的线程这个时候会进入到Monitor的锁池(entryList)里面等待状态为Blocked阻塞,锁池是一个队列,先进先出。当然Monitor里不光是只有锁池,还有等待池,当owner释放了锁【可以理解为owner=null】这个时候,线程如果调用wait()方法,由此可见,synchronized只有在获得锁之后才能调用wait()方法,调用wait()方法的线程就会进入等待池里面,线程状态就会变成waiting,然后线程如果调用notify()方法,就会进入锁池(entryList),状态就会重新变成Blocked状态;当然如果代码运行完了,锁就会自动释放。

    以上原理有点绕,但不是瞎编乱造,可以去hotspot官网下载源码来看,大部分看不懂,但是有一些是能看懂的在这里插入图片描述

    三、Lock详解

    Lock接口的方法签名:

    void lock():  获取锁(获取不到一直获取)
    boolead tryLock():  获取锁(获取不到我就不获取了)
    boolean tryLock(long time,TimeUnit unit) throws InterruptedException (获取锁,有等待时间,过时就不获取了)
    void lockInterruptibly() throws InterruptedException;获取锁 (可以被其他线程阻断获取)
    void unlock(); 释放锁
    Condition newContidion():  挂起或唤醒线程
    

    其中lock()最常用;

    复习一下线程通信的内容:
    在这里插入图片描述
    Object中的wait()/notify()、notifyAll()只能和synchronizde配合使用,可以唤醒一个或者多个线程;condetion需要和Lock配合使用,提供等待线程集合,可以更精准。

    好的来看代码演示:

    package com.nipx.demo.CAS;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Condition_Demo1 {
        private static Lock lock = new ReentrantLock();
        private static Condition condition = lock.newCondition();
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    try {
                        System.out.println("线程开始挂起......");
                        condition.await();
                        System.out.println("线程开始释放......");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            });
            thread.start();
            Thread.sleep(5000L);
    
            condition.signal();
        }
    }
    
    

    在这里插入图片描述
    报错了:原因就是我们的condition不管是挂起还是唤醒,都必须和lock配合使用然后我们修改代码:

    package com.nipx.demo.CAS;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Condition_Demo1 {
        private static Lock lock = new ReentrantLock();
        private static Condition condition = lock.newCondition();
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    try {
                        System.out.println("线程开始挂起......");
                        condition.await();
                        System.out.println("线程开始释放......");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            });
            thread.start();
            Thread.sleep(5000L);
            lock.lock();
            condition.signal();
            lock.unlock();
        }
    }
    
    

    在这里插入图片描述
    可以了。

    然后我们演示死锁的代码,线程先唤醒,再挂起。

    package com.nipx.demo.CAS;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Condition_Demo1 {
        private static Lock lock = new ReentrantLock();
        private static Condition condition = lock.newCondition();
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(5000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lock.lock();
                    try {
                        System.out.println("5秒之后线程开始挂起等待......");
                        condition.await();
                        System.out.println("线程开始释放......");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            });
            thread.start();
            Thread.sleep(2000L);
            lock.lock();
            System.out.println("2秒之后线程先唤醒.....");
            condition.signal();
            lock.unlock();
        }
    }
    
    

    在这里插入图片描述
    然后我们用condition来实现一个阻塞队列:
    /**
    要求:阻塞队列只能存储n个元素
    take 时:若队列有元素就直接获取;如果没有,则等待元素
    put 时:若队列未满,就直接put;如果已满,则阻塞,等到再有空间去put
    */

    package com.nipx.demo.CAS;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Condition_Demo2 {
        private static Lock lock = new ReentrantLock();
        private static Condition putCondition = lock.newCondition();
        private static Condition takeCondition = lock.newCondition();
        List<Object> list = new ArrayList();
        private int length;
        public Condition_Demo2(int length) {
            this.length = length;
        }
        public static void main(String[] args) throws InterruptedException {
            Condition_Demo2 demo2 = new Condition_Demo2(4);
            new Thread(() -> {
                for (int i = 0; i < 20; i++) {
                    try {
                        demo2.put("元素:" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
    
            Thread.sleep(3000L);
            for (int i = 0; i < 10; i++) {
                demo2.take();
                Thread.sleep(2000L);
            }
        }
        public void put(Object object) throws InterruptedException {
            lock.lock();
            try {
                while (true) {
                    if (list.size() < length) {
                        list.add(object);
                        takeCondition.signal();
                        System.out.println("put" + object);
                        return;
                    } else {
                        putCondition.await();
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    
        public Object take() {
            lock.lock();
            try {
                while (true) {
                    if (list.size() > 0) {
                        Object ob = list.remove(0);
                        putCondition.signal();
                        System.out.println("take" + ob);
                        return ob;
                    } else {
                        takeCondition.await();
                    }
                }
            } finally {
                lock.unlock();
                return null;
            }
        }
    }
    
    执行结果如下:
    

    在这里插入图片描述

    四、ReentrantLock底层原理

    1、ReentrantLock是一把可重入锁,它的内存空间里面有3个东西
    waiters: 等待池
    owner: 锁持有者
    count: 锁重入次数
    多个线程来抢锁的时候,是根据CAS原理实现的,抢锁成功的线程,把自己的名字记 录到owner,然后执行count+1;未抢到锁的线程这个时候会进入到waiters(此时线程状态:waiting)等待池中,再次抢到锁的线程会修改count+1然后修改owner;如果是同一个线程再次抢到了锁,会直接进行count+1。

    2、自己实现一把可重入锁ReentrantLock代码如下:

    package com.nipx.demo.CAS;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.LockSupport;
    
    public class ReentrantLock implements Lock {
        // 锁持有者 owner
        private AtomicReference<Thread> owner = new AtomicReference<>();
    
        // 可重入次数
        private AtomicInteger count = new AtomicInteger(0);
    
        // 等待池
        private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue();
    
    
        @Override
        public boolean tryLock() {
            int ct = count.get();
            //判断count值是否为0,如果不为0,则说明锁被占用
            if (ct != 0) {
                //判断是否是当前线程占用,做重入
                if (Thread.currentThread() == owner.get()) {
                    count.set(ct + 1);
                    return true;
                }
            } else {// 判断count值如果为0,则通过CAS来抢锁
                if (count.compareAndSet(ct, ct + 1)) {
                    owner.set(Thread.currentThread());
                }
            }
            return false;
        }
    
        @Override
        public void lock() {
            if (!tryLock()) {
                //加入等待队列
                waiters.offer(Thread.currentThread());
                while (true) {//自旋
                    //如果线程是队列的头部,则尝试加锁
                    Thread thread = waiters.peek();
                    if (thread == Thread.currentThread()) {
                        if (!tryLock()) {
                            LockSupport.park();
                        } else {
                            waiters.poll();
                            return;
                        }
                    } else {
                        LockSupport.park();
                    }
                }
    
            }
    
        }
    
        public boolean tryUnlock() {
            if (owner.get() != Thread.currentThread()) {
                throw new IllegalMonitorStateException();
            } else {
                //unlock 就是将count-1
                int newCount = count.get() - 1;
                count.set(newCount);
                //如果减1之后为0,说明成功释放锁
                if (newCount == 0) {
                    owner.compareAndSet(Thread.currentThread(), null);
                    return true;
                } else {
                    return false;
                }
            }
        }
    
        @Override
        public void unlock() {
            if (!tryUnlock()) {
                //获取头部的线程
                Thread head = waiters.peek();
                if (head != null) {
                    LockSupport.unpark(head);
                }
            }
        }
    
        @Override
        public void lockInterruptibly() throws InterruptedException {
    
        }
    
    
        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
    
        @Override
        public Condition newCondition() {
            return null;
        }
    }
    
    

    五、读写锁ReentrantReadWriterLock

    1、主要目的就是解决了性能问题,正常的读和写是互斥的,每次只能有一个线程去执行读或者写,这样就产生了性能问题,读写锁主要让读和写继续互斥,但是把读锁变成共享锁,写操作只需要一个线程操作,而读操作可以是多个线程来操作。

    首先明确一点,Hashtable的源码解释是这样的:
    在这里插入图片描述
    【{@code Hashtable}是同步的。如果一个不需要线程安全实现,建议使用

    • {@link HashMap}代替了{@code Hashtable}。
    • 如果一个线程安全的需要高并发的实现,然后推荐使用
      *使用{@link java.util.concurrent。ConcurrentHashMap}代替】

    也就是说JDK其实是不建议用hashtable的,对于高并发,它建议我们使用ConcurrentHashMap;如何用读写锁把HashMap变得线程安全?

    代码如下:

    package com.nipx.demo.CAS;
    import java.util.HashMap;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    public class HashMap_Demo {
        private final HashMap<String, Object> map = new HashMap<>();
        private final ReadWriteLock writeLock = new ReentrantReadWriteLock();
        private final Lock r = writeLock.readLock();
        private final Lock w = writeLock.writeLock();
        public Object put(String key, Object value) {
            w.lock();
            try { 
                return map.put(key, value);
            } finally {
                w.unlock();
            }
        }
    
        public Object get(Object key) {
            r.lock();
            try {
                return map.get(key);
            } finally {
                r.unlock();
            }
        }
        public void clear() {
            w.lock();
            try {
                map.clear();
            } finally {
                w.unlock();
            }
        }
    }
    

    2、读写锁的原理
    读写锁是两把锁,一把是读锁,一把是写锁,他和ReentrantLock差不多,也有waiters等待池,owner和count;但是count分为readCount和writerCount。
    waiters:等待池
    owner:锁拥有着
    readCount:读锁次数
    writerCount:写锁次数

    假设现在又线程t1、t2、t3、t4。

    如果现在t1线程去抢写锁,那么它会判断此时读锁里面的readCount是否为0,如果不为零,那么表示此时有线程在持有读锁,t1线程就会去等待池中;如果为0,那么t1线程就会抢锁成功,writerCount+1之后会把owner修改为自己,t2线程这时也要抢写锁,它首先也得判断此时读锁里面的readCount是否为0,如果为0,那么它就会进行CAS操作去抢写锁,抢锁失败就会去等待池中等待。

    如果现在t3线程去抢读锁,那么它首先会判断此时写锁里面的writerCount是否为0,如果不为0,那么它说明此时有线程持有写锁,这个时候t3会对owner进行判断,如果是自己,那么进行锁降级,写锁降级为读锁;如果不是自己,那么t3就去等待池等待;如果为0,那么就去抢读锁,成功之后只会把readCount+1,并不会修改owner;t4线程这时候如果也来抢占读锁,判断此时写锁里面的writerCount是为0后,那么t4也会执行readCount+1。

    这样就形成了单写多读的状态。

    六、synchronized和lock的区别

    synchronized和lock的区别在哪

    1、synchronized:
    【优点】:
    a.使用简单,语义清晰,哪里需要加哪里。
    b.由jvm提供,提出了很多优化(锁粗话,锁消除,偏向锁)
    c.锁的释放由虚拟机提供,无人工化,降低了死锁的可能性
    【缺点】
    a.无法实现一些高级的功能,例如:公平锁、中断锁、超时锁、读写锁、共享锁

    2、lock:
    【优点】:所有synchronized的缺点
    【缺点】:需要手动释放锁

    展开全文
  • java

    千次阅读 2018-10-23 23:39:22
    Java对象保存在内存中时,由以下三部分组成: 1,对象头 2,实例数据 3,对齐填充字节 一,对象头 java的对象头由以下三部分组成: 1,Mark Word 2,指向类的指针 3,数组长度(只有数组对象才有)   1,...

    目录

    一,对象头

    1,Mark Word

    2,指向类的指针

    3,数组长度

    二,实例数据

    三,对齐填充字节

    Java对象保存在内存中时,由以下三部分组成:

    1,对象头

    2,实例数据

    3,对齐填充字节

    一,对象头
    java的对象头由以下三部分组成:

    1,Mark Word

    2,指向类的指针

    3,数组长度(只有数组对象才有)

     

    1,Mark Word
    Mark Word记录了对象和锁有关的信息,当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和Mark Word有关。

    Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit。

    Mark Word在不同的锁状态下存储的内容不同,在32位JVM中是这么存的:

    锁状态

    25bit

    4bit

    1bit

    2bit

    23bit

    2bit

    是否偏向锁

    锁标志位

    无锁

    对象的HashCode

    分代年龄

    0

    01

    偏向锁

    线程ID

    Epoch

    分代年龄

    1

    01

    轻量级锁

    指向栈中锁记录的指针

    00

    重量级锁

    指向重量级锁的指针

    10

    GC标记

    11

    其中无锁和偏向锁的锁标志位都是01,只是在前面的1bit区分了这是无锁状态还是偏向锁状态。

    JDK1.6以后的版本在处理同步锁时存在锁升级的概念,JVM对于同步锁的处理是从偏向锁开始的,随着竞争越来越激烈,处理方式从偏向锁升级到轻量级锁,最终升级到重量级锁。

     

    JVM一般是这样使用锁和Mark Word的:

    1,当没有被当成锁时,这就是一个普通的对象,Mark Word记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。

    2,当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。

    3,当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。

    4,当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。

    5,偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。

    6,轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。

    7,自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。

     

    2,指向类的指针
    该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。

    Java对象的类数据保存在方法区。

     

    3,数组长度
    只有数组对象保存了这部分数据。

    该数据在32位和64位JVM中长度都是32bit。

     

    二,实例数据
    对象的实例数据就是在java代码中能看到的属性和他们的值。

     

    三,对齐填充字节
    因为JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数,没有特别的功能。

     

    以上。
    --------------------- 
    作者:lkforce 
    来源:CSDN 
    原文:https://blog.csdn.net/lkforce/article/details/81128115 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • Java锁机制

    千次阅读 多人点赞 2019-06-20 12:57:09
    Java锁的划分 Java锁具体可分为悲观锁/乐观锁、自旋锁/适应性自旋锁、偏向锁、轻量级锁/重量级锁、公平锁和非公平锁、可重入锁/非可重入锁、共享锁/排他锁 具体划分如下: 乐观锁VS悲观锁 特征 对于同一个...

    Java锁的划分

    Java锁具体可分为悲观锁/乐观锁、自旋锁/适应性自旋锁、偏向锁、轻量级锁/重量级锁、公平锁和非公平锁、可重入锁/非可重入锁、共享锁/排他锁
    具体划分如下:
    在这里插入图片描述

    乐观锁VS悲观锁

    概念

    • 对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java中,synchronized关键字和Lock的实现类都是悲观锁。
    • 乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据(可以使用版本号机制和CAS算法实现)。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)

    在这里插入图片描述

    应用场景

    • 悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。
    1. 传统的关系型数据库使用了很多悲观锁机制如行锁,表锁都是操作之前先上锁
    2. Java中的synchronized和ReentrantLock体现的就是悲观锁
    • 乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升
    1. java.util.concurrent.atomic包下面的原子变量类就是基于CAS实现的乐观锁

    优缺点

    乐观锁缺点

    1. ABA问题
      如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。
      J.U.C 包提供了一个带有标记的原子引用类 AtomicStampedReference 来解决这个问题, 它可以通过控制变量值的版本来保证 CAS 的正确性。 大部分情况下 ABA 问题不会影响程序并发的正确性, 如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。

    2. 自旋时间长开销大
      自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。 如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用, 第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源, 延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。 第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation) 而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

    3. 只能保证一个共享变量的原子操作 CAS只对单个共享变量有效,当操作涉及跨多个共享变量时CAS无效。 但是从 JDK 1.5开始,提供了AtomicReference类来保证引用对象之间的原子性, 可以把多个变量封装成对象里来进行 CAS 操作. 所以我们可以使用锁或者利用AtomicReference类把多个共享变量封装成一个共享变量来操作。

    示例

    // ------------------------- 悲观锁的调用方式 -------------------------
    // synchronized
    public synchronized void testMethod() {
    	// 操作同步资源
    }
    // ReentrantLock
    private ReentrantLock lock = new ReentrantLock(); // 需要保证多个线程使用的是同一个锁
    public void modifyPublicResources() {
    	lock.lock();
    	// 操作同步资源
    	lock.unlock();
    }
    
    // ------------------------- 乐观锁的调用方式 -------------------------
    private AtomicInteger atomicInteger = new AtomicInteger();  // 需要保证多个线程使用的是同一个AtomicInteger
    atomicInteger.incrementAndGet(); //执行自增1
    
    • 悲观锁显式进行锁定只同步操作

    自旋锁 VS 适应性自旋锁

    自旋锁概念

    • 阻塞或者唤醒一个JAVA的线程需要操作系统切换CPU状态来完成,这种状态的转换需要耗费处理器时间。如果同步代码块中的内容过于简单,很可能导致状态转换消耗的时间比用户代码执行的时间还要长。所以在短暂的等待之后就可以继续进行的线程,为了让线程等待一下,需要让线程进行自旋,在自旋完成之后,前面锁定了同步资源的线程已经释放了锁,那么当前线程就可以不需要阻塞便直接获取同步资源,从而避免了线程切换的开销。这就是自旋锁。
      在这里插入图片描述

    在这里插入图片描述

    自旋锁优缺点

    • 优点: 阻塞或唤醒一个 Java 线程需要操作系统切换 CPU 状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长。所以自旋锁就可以避免 CPU 切换带来的性能、时间等影响。
    • 缺点: 自旋等待虽然避免了线程切换的开销,但它要占用处理器时间。如果锁被占用的时间很短,自旋等待的效果就会非常好。反之,如果锁被占用的时间很长,那么自旋的线程只会白浪费处理器资源。所以,自旋等待的时间必须要有一定的限度,如果自旋超过了限定次数(默认是 10 次,可以使用 -XX:PreBlockSpin 来更改)没有成功获得锁,就应当挂起线程

    适应性自旋锁

    • 自适应意味着自旋的时间(次数)不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也是很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。如果对于某个锁,自旋很少成功获得过,那在以后尝试获取这个锁时将可能省略掉自旋过程,直接阻塞线程,避免浪费处理器资源。

    示例

    • AtomicInteger 是基于 CAS 实现的自旋锁
    // JDK 8
    // AtomicInteger 自增方法
    public final int incrementAndGet() {
      return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
    // Unsafe.class
    public final int getAndAddInt(Object var1, long var2, int var4) {
      int var5;
      do {
          var5 = this.getIntVolatile(var1, var2);
      } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
      return var5;
    }
    

    无锁VS偏向锁

    无锁

    • 无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。
    • 无锁的特点就是修改操作在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。CAS原理及应用即是无锁的实现。无锁无法全面代替有锁,但无锁在某些场合下的性能是非常高的。

    偏向锁

    • 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。
    • 在大多数情况下,锁总是由同一线程多次获得,不存在多线程竞争,所以出现了偏向锁。其目标就是在只有一个线程执行同步代码块时能够提高性能。
    • 在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。
    • 偏向锁在JDK 6及以后的JVM里是默认启用的。可以通过JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,关闭之后程序默认会进入轻量级锁状态。

    偏向锁的实现

    偏向锁的获取

    1. 访问Mark Word中偏向锁的标识是否设置成1,锁标志位是否为01,确认为可偏向状态。
    2. 如果为可偏向状态,则测试线程ID是否指向当前线程,如果是,进入步骤5,否则进入步骤3。
    3. 如果线程ID并未指向当前线程,则通过CAS操作竞争锁。如果竞争成功,则将Mark Word中线程ID设置为当前线程ID,然后执行5;如果竞争失败,执行4。
    4. 如果CAS获取偏向锁失败,则表示有竞争。当到达全局安全点(safepoint)时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。(撤销偏向锁的时候会导致stop the word)
    5. 执行同步代码。

    偏向锁的释放

    * 偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动去释放偏向锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。

    轻量级锁VS重量级锁

    概念

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

    轻量锁的加锁过程

    1. 在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,官方称之为 Displaced Mark Word
    2. 拷贝对象头中的Mark Word复制到锁记录中
    3. 拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock record里的owner指针指向object mark word。如果更新成功,则执行步骤4,否则执行步骤5
    4. 如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态
    5. 如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 而当前线程便尝试使用自旋来获取锁,自旋就是为了不让线程阻塞,而采用循环去获取锁的过程

    公平锁VS非公平锁

    概念

    • 公平锁(Fair):加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得
    • 非公平锁(Nonfair):加锁时不考虑排队等待问题,直接尝试获取锁,如果的锁的状态可用,那么该线程会跳过所有等待直接获取锁,相当于有插队行为

    优缺点

    • 公平锁CPU唤醒阻塞线程的开销比非公平锁大,恢复一个挂起的线程到线程真正的去运行存在严重延迟。假设线程A持有一个锁,并且线程B请求这个锁。由于锁被A持有,因此B将被挂起。当A释放锁时,B将被唤醒,因此B会再次尝试获取这个锁。与此同时,如果线程C也请求这个锁,那么C很可能会在B被完全唤醒之前获得、使用以及释放这个锁。这样就是一种双赢的局面:B获得锁的时刻并没有推迟,C更早的获得了锁,并且吞吐量也提高了
    • 当持有锁的时间相对较长或者请求锁的平均时间间隔较长,应该使用公平锁。在这些情况下,插队带来的吞吐量提升(当锁处于可用状态时,线程却还处于被唤醒的过程中)可能不会出现。

    示例

    • ReentrantLock 默认的lock()方法采用的是非公平锁
        /**
         * Creates an instance of {@code ReentrantLock} with the
         * given fairness policy.
         *
         * @param fair {@code true} if this lock should use a fair ordering policy
         */
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    
    • 公平锁有个!hasQueuedPredecessors()条件,意思是说当前同步队列没有前驱节点(也就是没有线程在等待)时才会去compareAndSetState(0, acquires)使用CAS修改同步状态变量。所以就实现了公平锁,根据线程发出请求的顺序获取锁
        static final class FairSync extends Sync {
            private static final long serialVersionUID = -3000897897090466540L;
    
            final void lock() {
                acquire(1);
            }
    
            /**
             * Fair version of tryAcquire.  Don't grant access unless
             * recursive call or no waiters or is first.
             */
            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;
            }
        }
    
    • 非公平锁的实现在刚进入lock方法时会直接使用一次CAS去尝试获取锁,不成功才会到acquire方法,而在nonfairTryAcquire方法中并没有判断是否有前驱节点在等待,直接CAS尝试获取锁
    	  /**
    	 * Performs lock.  Try immediate barge, backing up to normal
    	 * acquire on failure.
    	 */
    	final void lock() {
    		if (compareAndSetState(0, 1))
    			setExclusiveOwnerThread(Thread.currentThread());
    		else
    			acquire(1);
    	}
        public final void acquire(int arg) {
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
        protected final boolean tryAcquire(int acquires) {
              return nonfairTryAcquire(acquires);
        }
     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) // overflow
    			throw new Error("Maximum lock count exceeded");
    		setState(nextc);
    		return true;
    	}
    	return false;
    }
    

    可重入锁 VS 非可重入锁

    概念

    • 可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁
    • 不可重入锁,即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞
    • 不可重入锁,可能存在被当前线程所持有,且无法释放的死锁问题

    独享锁VS共享锁

    • 独享锁:该锁每一次只能被一个线程所持有,也叫排他锁,获得排他锁的 线程既能读数据又能写数据
    • 共享锁:该锁可被多个线程共有,典型的就是ReentrantReadWriteLock里的读锁,它的读锁是可以被共享的,但是它的写锁确每次只能被独占
    • 独享锁与共享锁时通过AQS来实现 的

    示例

    • 独享锁ReentrantLock源码中可以看出无论是公平锁还是非公平锁,只要当线程不是拥有锁的线程,都不能加锁
     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) // overflow
    			throw new Error("Maximum lock count exceeded");
    		setState(nextc);
    		return true;
    	}
    	return false;
    }
    
    • 共享锁tryAcquireShared(int unused)方法中,如果其他线程已经获取了写锁,则当前线程获取读锁失败,进入等待状态.如果当前线程获取了写锁或者写锁未被获取,则当前线程(线程安全,依靠CAS保证)增加读状态,成功获取读锁。读锁的每次释放(线程安全的,可能有多个读线程同时释放读锁)均减少读状态,减少的值是“1<<16”。所以读写锁才能实现读读的过程共享,而读写、写读、写写的过程互斥

    • protected final int tryAcquireShared(int unused) {
      Thread current = Thread.currentThread();
      int c = getState();
      if (exclusiveCount© != 0 &&
      getExclusiveOwnerThread() != current)
      return -1;
      int r = sharedCount©;
      if (!readerShouldBlock() &&
      r < MAX_COUNT &&
      compareAndSetState(c, c + SHARED_UNIT)) {
      if (r == 0) {
      firstReader = current;
      firstReaderHoldCount = 1;
      } else if (firstReader == current) {
      firstReaderHoldCount++;
      } else {
      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;
      }
      return fullTryAcquireShared(current);
      }

    引申

    1. CAS原理
      https://blog.csdn.net/weixin_41950473/article/details/93994332
    2. AQS原理
    3. ReentrantLock锁
    4. 数据库的乐观锁和悲观锁
    5. Synchronized的底层优化
    6. ReentrantReadWriteLock原理及读锁与写锁

    参考

    展开全文
  • Java锁升级

    千次阅读 多人点赞 2020-04-06 01:36:52
    Java锁升级 对象内存布局 Java对象在内存中存储的布局可以分为3块区域: 对象头、实例数据、对齐填充。 对象头,分为两个部分,第一个部分存储对象自身的运行时数据,又称为Mark Word,32位虚拟机占32bit,64位虚拟机占...

    Java锁

    锁概念

    公平锁与非公平锁

    公平锁:线程获取锁的顺序与其申请顺序一致

    非公平锁:线程获取锁的顺序并不是按照申请锁顺序,有可能后申请锁的线程先获取锁。

    可重入锁与不可重入锁

    可重入锁:线程获取锁后,可以重复进入同步代码区,不需要重复申请锁

    不可重入锁:线程每次进入同步代码区时,均需要获取锁,即使已经获取过该锁。

    悲观锁与乐观锁

    悲观锁:总是持有悲观的态度,认为并发冲突一般会发生

    乐观锁:总是持有乐观的态度,认为并发冲突一般不会发生

    锁消除与锁粗化

    锁消除:消除是Java虚拟机在JIT编译期间,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过锁消除,可以节省毫无意义的请求锁时间。

    锁粗化:在遇到一连串地对同一锁不断进行请求和释放的操作时,把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数

    自旋锁和自适应自旋

    自旋锁:线程尝试获取锁,发现被占用后,不放弃cpu执行时间,进入阻塞状态,而是进入进行循环重试,重试指定次数后,仍未获取锁,则进入阻塞过程。

    自适应自旋:重试次数不固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。

    Java锁升级

    对象内存布局

    Java对象在内存中存储的布局可以分为3块区域: 对象头实例数据对齐填充

    对象头,分为两个部分,第一个部分存储对象自身的运行时数据,又称为Mark Word,32位虚拟机占32bit,64位虚拟机占64bit。如图所示,不同锁状态下,Mark Word的结构,理解下面要介绍的各种锁,和锁升级过程,都需要先充分了解Mark Word的结构。

    第二部分是类型指针,指向类元数据指针,虚拟机通过此指针,确定该对象属于那个类的实例。

    偏向锁

    引入偏向锁的目的是在没有多线程竞争的前提下,进一步减少线程同步的性能消耗。

    • 偏向锁的获取

      开启偏向锁模式后,锁第一次被线程获取的时候,虚拟机会把对象头中是否为偏向锁的标志位设位1,

      同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中。当有另外一个线程去尝试获取这个锁时, 偏向模式就宣告结束。

      根据锁对象目前是否处于被锁定的状态, 撤销偏向( Revoke Bias) 后恢复到未锁定( 标志位为“01”)或轻量级锁定( 标志位为“00”) 的状态

    • 偏向锁的释放

      偏向锁,并没有显式的锁释放过程,主要依靠锁的批量再偏向(Bulk Rebias)机制实现锁释放。

      该机制的主要工作原理如下:

      • 引入一个概念 epoch, 其本质是一个时间戳 , 代表了偏向锁的有效性,从前文描述的对象头结构中可以看到,

        epoch 存储在可偏向对象的 MarkWord 中。

      • 除了对象中的 epoch, 对象所属的类 class 信息中, 也会保存一个 epoch 值,每当遇到一个全局安全点时, 如果要对 class 进行批量再偏向, 则首先对 class 中保存的 epoch 进行增加操作, 得到一个新的 epoch_new

      • 然后扫描所有持有 class 实例的线程栈,根据线程栈的信息判断出该线程是否锁定了该对象, 仅将epoch_new 的值赋给被锁定的对象中。

      • 退出安全点后, 当有线程需要尝试获取偏向锁时, 直接检查 class中存储的 epoch 值是否与目标对象中存储的 epoch 值相等,如果不相等, 则说明该对象的偏向锁已经无效了, 可以尝试对此对象重新进行偏向操作。

    轻量级锁

    轻量级锁是相对于重量级锁(Synchrnoized)而言的,本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

    • 轻量级锁的获取
      • 线程进入同步块时,如果此同步对象没有被锁定(即锁标志位为01,是否为偏向锁为0),虚拟机在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的一个Mark Word的copy ,ower设置为当前线程。

      • 然后虚拟机使用CAS操作,尝试将Mark World更新为指向Lock Record的指针,如果更新成功,那么线程拥有了该对象的锁,并且将锁标志位置位00,如图所示

      • 如果只有一个线程尝试获取轻量级锁,会进行自旋等待,一旦有两条以及以上的线程抢占该锁,轻量级锁会升级为重量级锁。

        锁标志位置为10,Mark Word存储的就是指向重量级锁的指针

    • 轻量级锁释放
      • 如果对象的Mark Word仍然指向着线程的锁记录, 那就用CAS操作把对象当前的Mark Word和线程中复制的Displaced Mark Word替换回来, 如果替换成功, 整个同步过程就完成了。如果替换失败, 说明有其他线程尝试过获取该锁,轻量级锁膨胀为重量级锁,那就要在释放锁的同时, 唤醒被挂起的线程。

    整个锁升级过程

    参考文章

    偏向锁,轻量级锁与重量级锁的区别与膨胀

    Java中的偏向锁,轻量级锁, 重量级锁解析

    展开全文
  • java锁消除和锁粗化

    万次阅读 2020-06-09 16:37:49
    什么是java锁消除呢?其实这些都是JVM帮我们做的功能,在JVM判断一个锁不会被其他线程使用,就会把锁消除来提高性能。 比如下面代码,我们知道StringBuffer的append方法上加入了synchronized关键字,所以是加了锁的...
  • Java锁消除和锁粗化

    万次阅读 多人点赞 2018-06-02 12:29:49
    转载自:Java锁消除非商业转载,可联系本人删除概述锁消除是Java虚拟机在JIT编译是,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过锁消除,可以节省毫无意义的请求锁时间。实验看如下代码:package...
  • Java锁之可重入锁和递归锁 目录 Java锁之可重入锁和递归锁基本概念 Java锁之可重入锁和递归锁代码验证 小结 理论,代码,小结,学习三板斧。 1. Java锁之可重入锁和递归锁基本概念 可重入锁(也...
  • java锁分为三大类乐观锁、悲观锁、自旋锁 乐观锁:乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有...
  • java锁机制

    2016-04-14 08:02:23
    1.java锁种类及相关概念 1、自旋锁 2、自旋锁的其他种类 3、阻塞锁 4、可重入锁 5、读写锁 6、互斥锁 7、悲观锁 8、乐观锁 9、公平锁 10、非公平锁 11、偏向锁 12、对象锁 13、线程锁 14、锁粗化 15、轻量级锁 16、...
  • Java锁的种类

    千次阅读 2019-04-17 16:55:21
    Java锁的种类 内置锁 Java 提供了一种内置的锁机制来支持原子性:同步代码块(Synchronized Block)。同步代码块包含两部分,一个作为锁的对象引用,一个作为由这个锁保护的代码块。以synchronized来修饰的方法就是一...
  • Java锁的种类: 公平锁、乐观锁、互斥锁、分段锁、偏向锁、自旋锁等
  • Java锁汇总

    千次阅读 2020-12-04 08:49:57
    1、悲观 1.1 进程 1.1.1 Java Synchronized 1.1.2 Java ReentrantLock 1.1.3 Java ReadWriteLock 1.2 分布式 1.2.1 基于DataBase分布式 1.2.2 基于Zookeeper分布式 1.2.3 基于Redis分布式 2、...
  • 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁乐观锁 VS 悲观锁1.乐观锁2.悲观锁3.总之公平锁 VS 非公平锁1.公平锁2.非公平锁3.典型应用独享锁 VS 共享锁1.独享锁2.共享锁3.比较4.AQS分段锁Java线程...
  • Java锁之公平和非公平锁

    千次阅读 2019-05-27 21:34:26
    Java锁之公平和非公平锁 目录 公平锁和非公平锁概念 公平锁和非公平锁区别 ReentrantLock和synchronized是公平锁还是非公平锁? 1. 公平锁和非公平锁概念 公平锁:是指多个线程按照申请锁的顺序来...
  • Java程序员面试必备,深入浅出Java锁优化。偏向锁,轻量级锁,锁消除,锁粗化,自旋锁最全总结
  • 转载自 java并发编程之4——Java锁分解锁分段技术   并发编程的所有问题,最后都转换成了,“有状态bean”的状态的同步与互斥修改问题。而最后提出的解决“有状态bean”的同步与互斥修改问题的方案是为所有修改...
  • 转载:JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁,如果需要查看具体的synchronized和lock的实现原理,请参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度) 在并发编程中,经常...
  • Java锁lock源码分析(三)读写锁

    千次阅读 2018-06-19 16:32:10
    Java锁lock源码分析(三)读写锁 前文Java锁Lock源码分析(一)提过在java的Lock中获取锁就表示AQS的volatile int state =1表示获取到了独占锁,state&amp;amp;amp;amp;amp;gt;1表示当前线程重入锁(获取锁了...
  • 详解Java锁机制:看完你就明白的锁系列之锁的状态 前言: 看完你就会知道,线程如果锁住了某个资源,致使其他线程无法访问的这种锁被称为悲观锁,相反,线程不锁住资源的锁被称为乐观锁,而自旋锁是基于 CAS 机制...
  • Java锁的几种应用

    千次阅读 2018-01-19 15:31:30
    Java锁常用概念:volatile/synchronized/ReentrantLock的区别
  • 面试必会系列 - 1.5 Java 机制

    千次阅读 2020-09-04 12:25:48
    Java 机制 概览 syncronized 升级过程 ReentrantLock 可重入 volatile 关键字 JUC 包下新的同步机制 syncronized 给一个变量/一段代码加锁,线程拿到之后,才能修改一个变量/执行一段代码 wait() notify...
  • Java锁实现

    千次阅读 2019-03-19 10:45:07
    Java编程语言允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新,线程应该确保通过排他单独获得这个变量。Java语言提供了volatile,在某些情况下比要更加方便。 volatile在多处理器开发中保证了...
  • Java 的理解

    千次阅读 2020-08-08 10:52:33
        用过并发包的朋友对这个应该不会陌生,ReentrantLock 是 Java 的 JUC(java.util.concurrent)包中提供的一种可重入,是一种递归无阻塞的同步机制。ReentrantLock 等同于synchronized关键字,但是 ...
  • Java锁详解

    千次阅读 2019-06-29 22:57:22
    文章目录什么是锁锁的实现方式涉及的几个重要概念类和对象(重要)synchronized实现原理 什么是 计算机还是单线程的时代,下面代码中的count,始终只会被一个线程累加,调用addOne()10次,count的值一定就...
  • Java锁Lock源码分析(二)条件锁

    千次阅读 2018-03-01 12:06:43
    本篇博文主要分析条件锁的源码实现、以及状态两个队列的变化: 1)Condition的使用场景 2)lock方法的队列(FIFO双向无环链表)官方点说是同步队列 sync queue ...本文是依赖于上篇博文Java锁...
  • java锁升级过程

    千次阅读 多人点赞 2020-03-14 20:32:17
    java中对象有4种状态:(级别从低到高) 1.无锁状态 2.偏向状态 3.轻量级状态 4.重量级状态 对象头分两部分信息,第一部分用于存储哈希码、GC分代年龄等,这部分数据被称为"Mark Word"。在32位的HotSpot...
  • Java锁类型

    千次阅读 2017-03-07 14:00:43
    转载链接在每个类型后边 线程类型 1、自旋 ,自旋,jvm默认是10次吧,有jvm自己控制。for去争取 作为并发共享数据,保证一致性的工具,在...本系列文章将分析JAVA下常见的名称以及特性,为大家答疑解惑。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 62,077
精华内容 24,830
关键字:

java锁

java 订阅