• Java多线程锁


    1.简述

      :把需要的代码块,资源或数据锁上,只允许一个线程去操作,保证了并发时共享数据的一致性。

    2.公平锁&非公平锁

      公平锁:多个线程按照申请锁的顺序去获得锁,所有线程都在队列里排队,这样就保证了队列中的第一个先得到锁。

      非公平锁:多个线程不按照申请锁的顺序去获得锁,而是同时直接去尝试获取锁(插队),获取不到(插队失败),再进入队列等待(失败则乖乖排队),如果能获取到(插队成功),就直接获取到锁。

      公平锁的优缺点

    • 优点:所有的线程都能得到资源,不会饿死在队列中。
    • 缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

      非公平锁的优缺点

    • 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
    • 缺点:可能导致队列中排队的线程一直获取不到锁或者长时间获取不到锁,活活饿死。

      性能

    • 非公平锁性能优于公平锁:因为公平锁在获取锁时,永远是等待时间最长的线程获取到锁,这样当线程释放锁以后,如果还想继续再获取锁,它也得去同步队列尾部排队,这样就会频繁的发生线程的上下文切换,当线程越多,对CPU的损耗就会越严重。非公平锁性能虽然优于公平锁,但是会存在导致线程饥饿的情况。在最坏的情况下,可能存在某个线程一直获取不到锁。不过相比性能而言,饥饿问题可以暂时忽略,这可能就是ReentrantLock默认创建非公平锁的原因之一了。

      以买票为例子实现公平锁与非公平锁demo如下

    public class Demo{
        //公平锁
        private static Lock fairLock = new ReentrantLock(true);
        //非公平锁
        private static Lock nonFairLock = new ReentrantLock();
        private static int num = 100;
    
        public static void main(String[] args) throws Exception {
            new Thread(new Runnable(){
                public void run() {
                    testFairLock();
                }
            }, "A窗口").start();
            new Thread(new Runnable(){
                public void run() {
                    testFairLock();
                }
            }, "B窗口").start();
        }
        
        /**公平锁测试方法
         */
        public static void testFairLock(){
            while (true) {
                try {
                    fairLock.lock();
                    if (num > 0) {
                        Thread.sleep(100);
                        --num;
                        System.out.println(Thread.currentThread().getName() + "占用1个座位,还剩余 " + num + "个座位");
                    } else {
                        System.out.println(Thread.currentThread().getName() + ":不好意思,票卖完了!");
                        break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    fairLock.unlock();
                }
            }
        }
        
        /**非公平锁测试方法
         */
        public static void testNonFairLock(){
            while (true) {
                try {
                    nonFairLock.lock();
                    if (num > 0) {
                        Thread.sleep(100);
                        --num;
                        System.out.println(Thread.currentThread().getName() + "占用1个座位,还剩余 " + num + "个座位");
                    } else {
                        System.out.println(Thread.currentThread().getName() + ":不好意思,票卖完了!");
                        break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    nonFairLock.unlock();
                }
            }
        }
    }
    View Code

      通过输出可以看出公平锁是有序的排队执行,而非公平则不是。

      对比公平锁与非公平锁的性能demo如下

    public class Demo{
        //公平锁
        private static Lock fairLock = new ReentrantLock(true);
        //非公平锁
        private static Lock nonFairLock = new ReentrantLock();
        //公平锁计数器
        private static int fairCount = 0;
        //非公平锁计数器
        private static int nonFairCount = 0;
    
        public static void main(String[] args) throws Exception {
            System.out.println("公平锁耗时:" + testFairLock(10));
            System.out.println("非公平锁耗时:" + testNonFairLock(10));
            System.out.println("公平锁累加结果:" + fairCount);
            System.out.println("非公平锁累加结果:" + nonFairCount);
        }
        
        /**公平锁测试方法
         */
        public static long testFairLock(int threadNum) throws InterruptedException{
            long startTime = System.currentTimeMillis();
            final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
            // 创建threadNum个线程,让其以公平锁的方式,对fairCount进行自增操作
            for (int i = 0; i < threadNum; i++) {
                new Thread(new Runnable(){
                    public void run() {
                        for (int j = 0; j < 10000; j++) {
                            fairLock.lock();
                            fairCount++;
                            fairLock.unlock();
                        }
                        countDownLatch.countDown();
                    }
                }).start();
            }
            // 让所有线程执行完
            countDownLatch.await();
            long endTime = System.currentTimeMillis();
            return endTime - startTime;
        }
        
        /**非公平锁测试方法
         */
        public static long testNonFairLock(int threadNum) throws InterruptedException{
            long startTime = System.currentTimeMillis();
            final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
            // 创建threadNum个线程,让其以公平锁的方式,对fairCount进行自增操作
            for (int i = 0; i < threadNum; i++) {
                new Thread(new Runnable(){
                    public void run() {
                        for (int j = 0; j < 10000; j++) {
                            nonFairLock.lock();
                            nonFairCount++;
                            nonFairLock.unlock();
                        }
                        countDownLatch.countDown();
                    }
                }).start();
            }
            // 让所有线程执行完
            countDownLatch.await();
            long endTime = System.currentTimeMillis();
            return endTime - startTime;
        }
    }
    View Code

      从上面的测试结果可以发现,非公平锁的耗时远远小于公平锁的耗时,这说明非公平锁在并发情况下,性能更好,吞吐量更大。当线程数越多时,差异越明显。

    3.可重入锁&不可重入锁

      可重入锁:也叫递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响。在Java中有ReentrantLock和synchronized。

      不可重入锁:若当前线程执行中已经获取了锁,如果再次获取该锁时,就会获取不到被阻塞。在Java中有NonReentrantLock。

      可重入锁诞生的目的就是防止死锁,导致同一个线程不可重入上锁代码段,目的就是让同一个线程可以重新进入上锁代码段

      可重入锁​synchronized示例

    public class Demo{
    
        public static void main(String[] args) throws Exception {
            new Thread(new Runnable() {
                public void run() {
                    synchronized (this) {
                        System.out.println("第1次获取锁,这个锁是:" + this);
                        int index = 1;
                        while (true) {
                            synchronized (this) {
                                System.out.println("第" + (++index) + "次获取锁,这个锁是:"+ this);
                            }
                            if (index == 10) {
                                break;
                            }
                        }
                    }
                }
            }).start();
        }
    }
    View Code

      可重入锁reentrantLock示例

    public class Demo{
    
        public static void main(String[] args) throws Exception {
            final Lock lock = new ReentrantLock();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println("第1次获取锁,这个锁是:" + lock);
                        int index = 1;
                        while (true) {
                            try {
                                lock.lock();
                                System.out.println("第" + (++index) + "次获取锁,这个锁是:"+ lock);
                                try {
                                    Thread.sleep(new Random().nextInt(200));
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                if (index == 10)
                                    break;
                            } finally {
                                lock.unlock();
                            }
                        }
                    } finally {
                        lock.unlock();
                    }
                }
            }).start();
        }
    }
    View Code

      注意点:ReentrantLock和synchronized不一样,ReentrantLock需要手动释放锁,所以使用 ReentrantLock的时候一定要手动释放锁,并且加锁次数和释放次数要一样,最好在 finally中进行锁释放

      自定义可重入所示例

    public class Demo{
    
        public static void main(String[] args) throws Exception {
            final Lock lock = new Lock();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println("第1次获取锁,这个锁是:" + lock);
                        int index = 1;
                        while (true) {
                            try {
                                lock.lock();
                                System.out.println("第" + (++index) + "次获取锁,这个锁是:"+ lock);
                                try {
                                    Thread.sleep(new Random().nextInt(200));
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                if (index == 10)
                                    break;
                            } catch (Exception e) {
                                System.out.println("第" + index + "次获取锁出现异常:"+ e.getMessage());
                            } finally {
                                lock.unlock();
                            }
                        }
                    } catch (Exception e) {
                        System.out.println("第1次获取锁出现异常:"+ e.getMessage());
                    } finally {
                        lock.unlock();
                    }
                }
            }).start();
        }
    }
    
    class Lock{
        boolean isLocked = false;
        Thread  lockedBy = null;
        int lockedCount = 0;
        public synchronized void lock()throws InterruptedException{
            Thread thread = Thread.currentThread();
            while(isLocked && lockedBy != thread){
                wait();
            }
            isLocked = true;
            lockedCount++;
            lockedBy = thread;
        }
        
        public synchronized void unlock(){
            if(Thread.currentThread() == this.lockedBy){
                lockedCount--;
                if(lockedCount == 0){
                    isLocked = false;
                    notify();
                }
            }
        }
    }
    View Code

      所谓可重入,意味着线程可以进入它已经拥有的锁的同步代码块儿。

      可重入锁,是指同一个线程不可以重入上锁后的代码段

      不可重入锁示例

    public class Demo{
    
        public static void main(String[] args) throws Exception {
            final NonReentrantLock lock = new NonReentrantLock();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        System.out.println("第1次获取锁,这个锁是:" + lock);
                        int index = 1;
                        while (true) {
                            try {
                                lock.lock();
                                System.out.println("第" + (++index) + "次获取锁,这个锁是:"+ lock);
                                try {
                                    Thread.sleep(new Random().nextInt(200));
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                if (index == 10)
                                    break;
                            } catch (Exception e) {
                                System.out.println("第" + index + "次获取锁出现异常:"
                                        + e.getMessage());
                            } finally {
                                lock.unlock();
                            }
                        }
                    } catch (Exception e) {
                        System.out.println("第1次获取锁出现异常:" + e.getMessage());
                    } finally {
                        lock.unlock();
                    }
                }
            }).start();
        }
    }
    
    class NonReentrantLock {
        private boolean isLocked = false;
    
        public synchronized void lock() throws InterruptedException {
            while (isLocked) {
                wait();
            }
            isLocked = true; //线程第一次进入后就会将器设置为true,第二次进入是就会由于where true进入死循环
        }
    
        public synchronized void unlock() {
            isLocked = false;//将这个值设置为false目的是释放锁
            notify();//结束阻塞
        }
    }
    View Code

      第一次上锁后,由于没有释放锁,因此执行第一次lock后isLocked = true,这个时候又一次调用了lock(),由于上个线程将isLocked = true,再次进入的时候就进入死循环。从而导致线程无法执行下去。这种现象就造成了不可重入锁。

    4.悲观锁&乐观锁

      悲观锁:悲观锁在操作数据时比较悲观,认为别人会同时修改数据。Java中Synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。

      乐观锁:乐观锁在操作数据时非常乐观,认为别人不会同时修改数据。在Java中java.util.concurrent.atomic包下面的原子变量类就是基于CAS实现的乐观锁。

      使用场景

    • 悲观锁:适用于读比较少的情况下(多写场景),如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行重试,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。
    • 乐观锁:适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。

      悲观锁的demo如下

    public class Demo{
    
        public static void main(String[] args) throws Exception {
            for (int i = 1; i <= 5; i++) {
                new Thread(new Runnable(){
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + ":等待");
                        visit();
                    }
                }, i+"号线程").start();
            }
        }
        
        private static synchronized void visit() {
            System.out.println(Thread.currentThread().getName()+ ":操作数据 ");
            try {
                Thread.sleep((long) (Math.random() * 5000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    View Code

      从输出可以看到,每次有线程访问这个资源visit()方法时,在访问前先锁定了资源,导致其他线程只能等待,也就是说不能多个线程在访问它。

      乐观锁可以使用版本号机制CAS算法实现。

      乐观锁:乐观锁不需要线程挂起等待,所以也叫非阻塞同步。

      版本号机制一般在一个数据表中加一个version字段,表示这个数据被更新的次数,当这个数据被修改一次,版本号就加1。

      版本号机制条件:提交版本必须大于当前记录的版本。

      版本号机制实现例子如下

    我现在银行账户有10元,现在有一个version字段,版本号为1。
    
    现在我A操作取出2元,我先读入数据version=1,然后扣除。
    
    与此同时,B操作也要取出1元,读入数据version=1,然后扣除。
    
    这个时候,A操作完成,上传数据,版本号加1,version=2,这个版本大于当前的记录值1,所以更新操作完成。
    
    这个时候,B操作也完成了,也要更新,他的版本号加1,version=2,然后更新的时候发现这个版本号和当前记录的版本号相同,不满足提交版本号必须大于当前记录的版本号的条件,不允许更新。
    
    这个时候,B操作就要重新读入再重复之前的步骤。

      通过这样的方式,我们就保证了B操作不会将A操作所更新的值覆盖,保证了数据的同步。

      CAS算法:即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。

      CAS算法涉及到三个操作数

    • 需要读写的内存值V。
    • 进行比较的值A。
    • 拟写入的新值B。

      当且仅当V的值等于A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。

      使用AtomicBoolean原子类实现的一个无阻塞多线程争抢资源的模型,示例如下

    public class Demo{
        
        private static AtomicBoolean flag = new AtomicBoolean(true);
    
        public static void main(String[] args) throws Exception {
            new Thread(new Runnable() {
                public void run() {
                    test();
                }
            }, "线程1").start();
            new Thread(new Runnable() {
                public void run() {
                    test();
                }
            }, "线程2").start();
        }
        
        private static void test() {
            System.out.println(Thread.currentThread().getName()+":flag="+flag.get());
            if (flag.compareAndSet(true, false)){
                System.out.println(Thread.currentThread().getName()+":flag="+flag.get());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                flag.set(true);
            }else{
                System.out.println(Thread.currentThread().getName()+"重试机制:flag="+flag.get());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                test();
            }
        }
    }
    View Code

      示例中compareAndSet(true, false)方法可以拆开成compare(true)方法和Set(false)方法理解,是compare(true)等于true后,就马上设置共享内存为false,这个时候,其它线程无论怎么走都无法走到只有得到共享内存为true时的程序隔离方法区。

      但是这种得不到状态为true时使用递归算法是很耗cpu资源的,所以一般情况下,都会有线程sleep。

      CAS的缺点

    • CPU开销较大:在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
    • 不能保证代码块的原子性:CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证多个变量共同进行原子性的更新,就不得不使用Synchronized了。

    5.独享锁&共享锁

      独享锁和共享锁都是通过AQS队列来实现的,通过实现不同的方法,来实现独享或者共享。

      独享锁:该锁一次只能被一个线程所持有,参考synchronized以及JUC包下的ReentrantLock。

      共享锁:该锁可被多个线程所持有,参考JUC包下的ReentrantReadWriteLock。

    6.互斥锁&读写锁

      互斥锁:互斥锁与悲观锁、独占锁同义,表示某个资源只能被一个线程访问,其他线程不能访问,Java提供了两种互斥锁来解决在共享资源时存在的并发问题,一种方式是synchronized关键字,另一种方式是显式的使用Lock对象。

      读写锁:读写锁是一种技术,通过ReentrantReadWriteLock类来实现。为了提高性能, Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由 jvm 自己控制的。

      读写锁:

    • 读锁: 允许多个线程获取读锁,同时访问同一个资源。
    • 写锁: 只允许一个线程获取写锁,不允许同时访问同一个资源。

      synchronized同步代码块的方式实现互斥锁

    public class Demo {
        //数量
        private static int count = 100;
        
        public static void main(String[] args) {
            for (int i = 1; i <= 3; i++) {
                new Thread(new Runnable(){
                    public void run() {
                        while(true){
                            if(count > 1)
                                synchronizedTest();
                            else
                                break;
                        }
                    }
                },i+"号线程").start();
            }
        }
        
        private synchronized static void synchronizedTest(){
            try {
                System.out.println(Thread.currentThread().getName()+"剩余数量为:" + count--);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    View Code

      显式Lock锁方式实现互斥锁

    public class Demo {
        //数量
        private static int count = 100;
        private static Lock lock = new ReentrantLock();
        
        public static void main(String[] args) {
            for (int i = 1; i <= 3; i++) {
                new Thread(new Runnable(){
                    public void run() {
                        while(true){
                            if(count > 1)
                                synchronizedTest();
                            else
                                break;
                        }
                    }
                },i+"号线程").start();
            }
        }
        
        private static void synchronizedTest(){
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"剩余数量为:" + count--);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.unlock();
        }
    }
    View Code

      读写锁实现操作缓存

    class ReadWriteCache {
        //充当cache
        static Map<String, Object> map = new HashMap<String, Object>();
        //实例化读写锁对象
        static ReadWriteLock rwLock = new ReentrantReadWriteLock();
        //实例化读锁
        static Lock read = rwLock.readLock();
        //实例化写锁
        static Lock write = rwLock.writeLock();
    
        //获取缓存中值
        public static final Object get(String key) {
            read.lock();
            try {
                return map.get(key);
            } finally {
                read.unlock();
            }
        }
    
        //写缓存中值,并返回对应value
        public static final Object set(String key, Object obj) {
            write.lock();
            try {
                return map.put(key, obj);
            } finally {
                write.unlock();
            }
        }
    
        //清空所有内容
        public static final void clear() {
            write.lock();
            try {
                map.clear();
            } finally {
                write.unlock();
            }
        }
    }
    View Code

    7.分段锁

      分段锁:其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作(Java 8, JDK弃用了这个策略,重新使用了synchronized)。

      基于散列的Map的实现分段锁

    class StripedMap {
        // 同步策略: buckets[n]由 locks[n%N_LOCKS] 来保护
        private static final int N_LOCKS = 16;// 分段锁的个数
        private final Node[] buckets;
        private final Object[] locks;
    
        /**结点
         * @param <K>
         * @param <V>
         */
        private static class Node<K, V> implements Map.Entry<K, V> {
            final K key;// key
            V value;// value
            Node<K, V> next;// 指向下一个结点的指针
            int hash;// hash值
    
            // 构造器,传入Entry的四个属性
            Node(int h, K k, V v, Node<K, V> n) {
                value = v;
                next = n;// 该Entry的后继
                key = k;
                hash = h;
            }
    
            public final K getKey() {
                return key;
            }
    
            public final V getValue() {
                return value;
            }
    
            public final V setValue(V newValue) {
                V oldValue = value;
                value = newValue;
                return oldValue;
            }
        }
    
        /**构造器: 初始化散列桶和分段锁数组
         * @param numBuckets
         */
        public StripedMap(int numBuckets) {
            buckets = new Node[numBuckets];
            locks = new Object[N_LOCKS];
            for (int i = 0; i < N_LOCKS; i++) {
                locks[i] = new Object();
            }
        }
    
        /**返回散列之后在散列桶之中的定位
         * @param key
         * @return
         */
        private final int hash(Object key) {
            return Math.abs(key.hashCode() % N_LOCKS);
        }
    
        /**分段锁实现的get
         * @param key
         * @return
         */
        public Object get(Object key) {
            int hash = hash(key);// 计算hash值
            // 获取分段锁中的某一把锁
            synchronized (locks[hash % N_LOCKS]) {
                for (Node m = buckets[hash]; m != null; m = m.next) {
                    if (m.key.equals(key)) {
                        return m.value;
                    }
                }
            }
            return null;
        }
    
        /**清除整个map
         */
        public void clear() {
            // 分段获取散列桶中每个桶地锁,然后清除对应的桶的锁
            for (int i = 0; i < buckets.length; i++) {
                synchronized (locks[i % N_LOCKS]) {
                    buckets[i] = null;
                }
            }
        }
    }
    View Code

      实现示例中使用了N_LOCKS个锁对象数组,并且每个锁保护容器的一个子集,对于大多数的方法只需要回去key值的hash散列之后对应的数据区域的一把锁就行了。但是对于某些方法却要获得全部的锁,比如clear()方法,但是获得全部的锁不必是同时获得,可以使分段获得。

    8.自旋锁

      自旋锁:是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。

      Java自旋锁:Jdk提供的java.util.concurrent.atomic包里面提供了一组原子类。基本上就是当前获取锁的线程,执行更新的方法,其他线程自旋等待,比如atomicInteger类中的getAndAdd方法内部实际上使用的就是Unsafe的方法。

      利用CAS实现自旋锁

    public class Demo {
        static int count = 0;
        public static void main(String[] args) throws Exception {
            //线程池
            ExecutorService exec = Executors.newFixedThreadPool(2);
            //任务集合
            List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();
            //任务
            Callable<Integer> task = null;
            final SpinLock spinLock = new SpinLock();
            for (int i = 0; i < 2; i++) {
                task = new Callable<Integer>() {
                    public Integer call() throws Exception {
                        while(count < 50){
                            spinLock.lock();
                            System.out.println(Thread.currentThread().getName()+" 开始运行");
                            count++;
                            Thread.sleep(500);
                            System.out.println(Thread.currentThread().getName()+" 结束运行");
                            spinLock.unLock();
                        }
                        return 1;
                    }
                };
                //这里提交的任务容器列表和返回的Future列表存在顺序对应的关系
                tasks.add(task);
            }
            //执行线程任务
            exec.invokeAll(tasks);
            //关闭线程池
            exec.shutdown();
            System.out.println(count);
        }
    }
    
    class SpinLock {
        /**持有锁的线程,null表示锁未被线程持有
         */
        private AtomicReference<Thread> cas = new AtomicReference<>();
    
        public void lock() {
            Thread currentThread = Thread.currentThread();
            while (!cas.compareAndSet(null, currentThread)) {//利用CAS
                //通过循环不断的自旋判断锁是否被其他线程持有
            }
            System.out.println(Thread.currentThread().getName()+" 获取锁");
        }
    
        public void unLock() {
            Thread cur = Thread.currentThread();
            cas.compareAndSet(cur, null);
            System.out.println(Thread.currentThread().getName()+" 释放锁");
        }
    }
    View Code

      实现示例中看出,自旋就是在循环判断条件是否满足,如果锁被占用很长时间的话,自旋的线程等待的时间也会变长,白白浪费掉处理器资源。因此在JDK中,自旋操作默认10次,我们可以通过参数“-XX:PreBlockSpin”来设置,当超过来此参数的值,则会使用传统的线程挂起方式来等待锁释放。

    9.偏向锁&轻量级锁&重量级锁

      锁的状态总共有四种,级别由低到高依次为:无锁、偏向锁、轻量级锁、重量级锁,只能升级,不能降级。

      偏向锁:偏向锁就是在运行过程中,对象的锁偏向某个线程。即在开启偏向锁机制的情况下,某个线程获得锁,当该线程下次再想要获得锁时,不需要再获得锁(即忽略synchronized关键词),直接就可以执行同步代码,比较适合竞争较少的情况。

      轻量级锁:轻量级锁不是用来替代传统的重量级锁的,而是在没有多线程竞争的情况下,使用轻量级锁能够减少性能消耗,但是当多个线程同时竞争锁时,轻量级锁会膨胀为重量级锁。

      重量级锁:即当有其他线程占用锁时,当前线程会进入阻塞状态。

      这些锁不等同于Java API中的ReentratLock这种锁,这些锁是概念上的,是JDK1.6中为了对synchronized同步关键字进行优化而产生的的锁机制。这些锁的启动和关闭策略可以通过设定JVM启动参数来设置,当然在一般情况下,使用JVM默认的策略就可以了。

  • 相关阅读:
    LeetCode-Maximum Gap
    LintCode-Implement Queue by Stacks
    LintCode-Search Range in Binary Search Tree
    LintCode-BackPack II
    LintCode-Minimum Subarray
    LintCode-Sort Letters by Case
    LintCode-Longest Common Subsequence
    POJ 2226
    POJ 2724
    POJ 3692
  • 原文地址:https://www.cnblogs.com/bl123/p/14875245.html
Copyright © 2020-2023  润新知