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();
    }
}

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值