synchronized可重入锁的实现原理:
synchronized底层的实现原理是利用计算机系统的mutex Lock实现。每一个可重入锁都会关联一个线程ID和一个锁状态status。
当一个线程请求方法时,会去检查锁状态,如果锁状态是0,代表该锁没有被占用,直接进行CAS操作获取锁,将线程ID替换成自己的线程ID。如果锁状态不是0,代表有线程在访问该方法。此时,如果线程ID是自己的线程ID,如果是可重入锁,会将status自增1,然后获取到该锁,进而执行相应的方法。如果是非重入锁,就会进入阻塞队列等待。
释放锁时,可重入锁,每一次退出方法,就会将status减1,直至status的值为0,最后释放该锁。
释放锁时,非可重入锁,线程退出方法,直接就会释放该锁。
所以,从一定程度上来说,可重入锁可以避免死锁的发生。
————————————————
原文链接:
https://blog.csdn.net/qq_39839075/article/details/100055872
代码证明:
package com.special.threademo.concurrency.synchcriozed;
/**
* @author liuYC
* @ClassName ReenTrantDemo
* @Description TODO
* @date 2021/7/20 16:17
* 说明:因为synchronized的作用在class上,当调用test1()中再次调用test2(),即证明了是可以重入的
*/
public class ReenTrantDemo {
public static void main(String[] args) {
System.out.println("start -------");
ReenTrantDemo.test1();
System.out.println("end -------");
}
public static synchronized void test1() {
System.out.println("test 1");
test2();
}
public static synchronized void test2() {
System.out.println("test 2 ");
// test2();
}
}
结论:
当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。
原因:
标记在对应的实例方法上和对应的 class 的区别!:从对象和类的分配位置说起:深度探索c++对象模型
很有可能不会加载,等到使用到的时候才会加载.
证明公平锁与否:
结论:
synchronized非公平锁:
公平锁:
获取不到锁的时候,会自动加入队列,等待线程释放后,队列的第一个线程获取锁
非公平锁:
获取不到锁的时候,会自动加入队列,等待线程释放锁后所有等待的线程同时去竞争
lock可指定公平与否
更加锁的可操作性、可中断的获取锁以及超时获取锁等多种synchronized不具备的特性。
注释基本明确,就不多说了。wait和notify是配合synchronized使用,await和signal是配合lock使用,区别在于唤醒时notify不能指定线程唤醒,signal可以唤醒具体的线程,更小的粒度控制锁。
代码展示:
package com.special.threademo.concurrency.synchcriozed; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author liuYC * @ClassName ReentrantFairDemo * @Description TODO * @date 2021/7/20 16:42 * 公平锁三个线程依次获取锁进行购票 */ public class ReentrantFairDemo { //总票数 private int count = 10; //创建锁 ReentrantLock可实现公平锁与非公平锁 private Lock reentrantLock = new ReentrantLock(true); // private Lock reentrantLock = new ReentrantLock();// 非公平锁 /* //获取余票数量方法 public void getCount() { while (true) { //锁住 reentrantLock.lock(); if (count > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName() + "卖票成功,余票为:" + count); } reentrantLock.unlock(); } }*/ //获取余票数量方法: // 证明的逻辑就是1234个线程顺序启动但是购票顺序却不一样,意味着获得锁的公平性不一样,所以非公平锁 // 且执行多次的顺序是不一样的: /** * 1卖票成功,余票为:9 * 4卖票成功,余票为:8 * 3卖票成功,余票为:7 * 2卖票成功,余票为:6 * <p> * <p> * demo2 * private static class DiningRoom { * //获取食物 * public void getFood() { * System.out.println(Thread.currentThread().getName()+":排队中"); * synchronized (this) { * System.out.println(Thread.currentThread().getName()+":@@@@@@打饭中@@@@@@@"); * } * } * } * 为什么执行多次都一样,难道说线程不够多么? */ /** * 没有加上对应的静态内部类的运行结果 * 1:排队中 * 1:@@@@@@打饭中@@@@@@@ * 2:排队中 * 2:@@@@@@打饭中@@@@@@@ * 3:排队中 * 3:@@@@@@打饭中@@@@@@@ * 4:排队中 * 4:@@@@@@打饭中@@@@@@@ * 5:排队中 * 5:@@@@@@打饭中@@@@@@@ * 6:排队中 * 6:@@@@@@打饭中@@@@@@@ */ // 获取食物d 注意静态内部类才有这个效果:原因就是因为锁在class上面???????????????????? private static class d2 { /** * 加上对应的静态内部类的运行结果 * 同学编号:001:排队中 * 同学编号:002:排队中 * 同学编号:001:@@@@@@打饭中@@@@@@@ * 同学编号:003:排队中 * 同学编号:002:@@@@@@打饭中@@@@@@@ * 同学编号:004:排队中 * 同学编号:003:@@@@@@打饭中@@@@@@@ * 同学编号:004:@@@@@@打饭中@@@@@@@ * 同学编号:005:排队中 * 同学编号:005:@@@@@@打饭中@@@@@@@ */ public void getCount() { System.out.println(Thread.currentThread().getName() + ":排队中"); synchronized (this) { System.out.println(Thread.currentThread().getName() + ":@@@@@@打饭中@@@@@@@"); } } } /** * 1排队中 * 2排队中 * 3排队中 * 4排队中 * 5排队中 * 6排队中 * 1卖票成功,余票为:9 * 6卖票成功,余票为:8 * 5卖票成功,余票为:7 * 4卖票成功,余票为:6 * 3卖票成功,余票为:5 * 2卖票成功,余票为:4 * * @param */ public void getCount() { //锁住 去掉while否则循环的结果是第一个线程一直在持有进行执行。 System.out.println(Thread.currentThread().getName() + "排队中"); synchronized (this) { if (count > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName() + "卖票成功,余票为:" + count); } } } public static void main(String[] args) { ReentrantFairDemo d = new ReentrantFairDemo(); final d2 dd22 = new d2(); //第1个线程 new Thread(new Runnable() { @Override public void run() { d.getCount(); } }, "1").start(); //第2个线程 new Thread(new Runnable() { @Override public void run() { d.getCount(); } }, "2").start(); //第33个线程 new Thread(new Runnable() { @Override public void run() { d.getCount(); } }, "3").start(); //第4个线程 new Thread(new Runnable() { @Override public void run() { d.getCount(); } }, "4").start(); new Thread(new Runnable() { @Override public void run() { d.getCount(); } }, "5").start(); new Thread(new Runnable() { @Override public void run() { d.getCount(); } }, "6").start(); // 创建线程的第二种方式:存在的原因:主要是为了验证,为什么执行的逻辑这么奇怪 for (int i = 0; i < 5; i++) { new Thread(() -> { dd22.getCount(); }, "同学编号:00" + (i + 1)).start(); } } }
问题为什么要用静态内部类才有这个效果:原因就是因为锁在class上面
否则没有明显的证据表明,随机的效果:
但是为什么只有这样才可以证明,如果那我不用这个呢?
存在队列的相关性顺序基本是:165432,无论运行多少次都这样
——————————
参考资料: