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();
    }
}
复制代码

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

 

posted @   请别耽误我写BUG  阅读(460)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示