• Java多线程学习之重入锁


    可重入锁的概念

    可重入:某个线程(Thread-A)已经获取到某个锁(lock-A),在该线程(Thread-A)未解锁前又再次获取到此锁(lock-A)而不出现死锁。

    可重入锁:能被某个线程在未解锁前重复获取而不出现死锁现象的锁。

    可重入锁的例子

    synchronized

    示例代码

    public class ReentryLock1 {
        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int i = 1;
                    synchronized (this) {
                        System.out.println("第" + i + "次获取锁,这个锁是:" + this);
                        for (i = 2; i < 10; i++) {
                            synchronized (this) {
                                System.out.println("第" + i + "次获取锁,这个锁是:" + this);
                            }
                        }
                    }
                }
            }).start();
        }
    }

    结果:可正常运行,无死锁现象,且锁对象是同一个

    ReentrantLock

    示例代码

    public class ReentryLock2 {
        public static void main(String[] args) {
            ReentrantLock lock = new ReentrantLock();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        int i = 1;
                        lock.lock();
                        System.out.println("第" + i + "次获取锁,这个锁是:" + lock);
                        for (i = 2; i < 10; i++) {
                            try {
                                lock.lock();
                                System.out.println("第" + i + "次获取锁,这个锁是:" + lock);
                            } finally {
                                lock.unlock();
    
                            }
                        }
                    } finally {
                        lock.unlock();
    
                    }
                }
            }).start();
        }
    }

    结果:可正常运行,无死锁现象,且锁对象是同一个

     Synchronized和ReentrantLock 对比

    1、性质不同:Synchronized是关键字,它的‘锁’是Java内置的功能;而ReentrantLock是一个实现Lock接口的类,需要调用方法lock()和unlock(),配合try/finally语句块来实现‘锁’功能;

    2、作用域不同:

    Synchronized

    修饰一个类:其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象;
    修饰一个方法:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
    修饰一个静态的方法:其作用的范围是整个方法,作用的对象是这个类的所有对象;
    修饰一个代码块:被修饰的代码块称为同步语句块,其作用范围是大括号{}括起来的代码块,作用的对象是调用这个代码块的对象。

    ReentrantLock

    修饰代码块:以方法lock()开始,以unlock()结束。

    3、结束方式不同:Synchronized一般是修饰的方法和代码块执行完以后才解锁,而ReentrantLock的结束取决于什么时候调用unlock()方法。

    4、性能不同:JDK6以前Synchronized还是重量级锁,性能很差,虽然从JDK6开始对Synchronized进行性能上的优化,但是高并发的情况下还是比ReentrantLock要差。(当然一般情况下Synchronized就足够满足大部分项目了,且使用方便,不用考虑手动解锁的问题)

    补充:

    ReentrantLock使用时尽量配合try/finally语句块来保证每次‘锁’都被手动‘解锁’,即lock()和unlock()调用方法次数要保持一致,不然可能会死锁。例如:

    数量一致时:同一个锁对象在被多次调用时可以正常运行。

    public class ReentryLock3 {
        public static void main(String[] args) {
            ReentrantLock lock = new ReentrantLock();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            lock.lock();
                            System.out.println("threadName:" + Thread.currentThread().getName());
                        } finally {
                            lock.unlock();
    
                        }
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            lock.lock();
                            System.out.println("threadName:" + Thread.currentThread().getName());
                        } finally {
                            lock.unlock();
    
                        }
                    }
                }
            }).start();
        }
    }

    数量不一致时:同一个锁对象在被多次调用时无法保证正常运行。

    public class ReentryLock3 {
        public static void main(String[] args) {
            ReentrantLock lock = new ReentrantLock();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //此处改为多次lock只在外面unlock一次用作测试
                    try {
                        for (int i = 0; i < 5; i++) {
                            lock.lock();
                            System.out.println("threadName:" + Thread.currentThread().getName());
                        }
                    } finally {
                        lock.unlock();
    
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            lock.lock();
                            System.out.println("threadName:" + Thread.currentThread().getName());
                        } finally {
                            lock.unlock();
    
                        }
                    }
                }
            }).start();
        }
    }

    结果:因为第一个线程未完全解锁,所以第二个线程无法正常获取到锁,导致死锁程序无法运行下去。

     

  • 相关阅读:
    SpringMVC+bootstrap-fileinput文件上传插件使用入门
    [Java]实现Comparable接口不严谨导致Comparison method violates its general contract!
    2021寒假ACM集训队第一次训练-搜索(一)
    第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛-热身赛
    2021蓝桥杯第三次训练赛
    2021年蓝桥杯第二次训练赛
    2021年蓝桥杯第一次训练赛
    HDU 1312 Red and Black
    HDU 1010 Tempter of the Bone
    HDU 3500 Fling
  • 原文地址:https://www.cnblogs.com/Bernard94/p/16242847.html
Copyright © 2020-2023  润新知