• 关于Synchronized锁的到底是什么元素


     八锁现象

    问题1 两个同步方法,先执行发短信还是打电话

    1. public class dome01 {
    2. public static void main(String[] args) {
    3. Phone phone = new Phone();
    4. new Thread(() -> { phone.sendMs(); }).start();
    5. TimeUnit.SECONDS.sleep(1);
    6. new Thread(() -> { phone.call(); }).start();
    7. }
    8. }
    9. class Phone {
    10. public synchronized void sendMs() {
    11. System.out.println("发短信");
    12. }
    13. public synchronized void call() {
    14. System.out.println("打电话");
    15. }
    16. }
    17. /*输出结果为
    18. 发短信
    19. 打电话
    20. */

    问题2: 我们再来看:我们让发短信 延迟4s

    1. public class dome01 {
    2. public static void main(String[] args) throws InterruptedException {
    3. Phone phone = new Phone();
    4. new Thread(() -> {
    5. try {
    6. phone.sendMs();
    7. } catch (InterruptedException e) {
    8. e.printStackTrace();
    9. }
    10. }).start();
    11. TimeUnit.SECONDS.sleep(1);
    12. new Thread(() -> { phone.call(); }).start();
    13. }
    14. }
    15. class Phone {
    16. //synchronized现在锁定的是方法的调用者,,而两个方法使用的是同一把锁。
    17. public synchronized void sendMs() throws InterruptedException {
    18. TimeUnit.SECONDS.sleep(4);
    19. System.out.println("发短信");
    20. }
    21. public synchronized void call() {
    22. System.out.println("打电话");
    23. }
    24. }
    25. //结果:还是先发短信,然后再打电话!

    原因:并不是顺序执行,而是synchronized 锁住的对象是方法的调用!对于两个方法用的是同一个锁, 谁先拿到谁先执行,另外一个等待

    问题三

    加一个普通方法

    1. public class dome01 {
    2. public static void main(String[] args) throws InterruptedException {
    3. Phone phone = new Phone();
    4. new Thread(() -> {
    5. try {
    6. phone.sendMs();
    7. } catch (InterruptedException e) {
    8. e.printStackTrace();
    9. }
    10. }).start();
    11. TimeUnit.SECONDS.sleep(1);
    12. new Thread(() -> { phone.hello(); }).start();
    13. }
    14. }
    15. class Phone {
    16. public synchronized void sendMs() throws InterruptedException {
    17. TimeUnit.SECONDS.sleep(4);
    18. System.out.println("发短信");
    19. }
    20. public synchronized void call() {
    21. System.out.println("打电话");
    22. }
    23. public void hello() {//这里没有synchronized,不受锁的影响。
    24. System.out.println("hello");
    25. }
    26. }
    27. //输出结果为
    28. hello
    29. 发短信

    原因:hello是一个普通方法,不受synchronized锁的影响,不用等待锁的释放

    问题四 如果我们使用的是两个对象,一个调用发短信,一个调用打电话,那么整个顺序是怎么样的呢?

    1. public class dome01 {
    2. public static void main(String[] args) throws InterruptedException {
    3. Phone phone1 = new Phone();
    4. Phone phone2 = new Phone();
    5. new Thread(() -> {
    6. try {
    7. phone1.sendMs();
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. }).start();
    12. TimeUnit.SECONDS.sleep(1);
    13. new Thread(() -> { phone2.call(); }).start();
    14. }
    15. }
    16. class Phone {//切记锁的是方法的调用者
    17. public synchronized void sendMs() throws InterruptedException {
    18. TimeUnit.SECONDS.sleep(4);
    19. System.out.println("发短信");
    20. }
    21. public synchronized void call() {
    22. System.out.println("打电话");
    23. }
    24. public void hello() {
    25. System.out.println("hello");
    26. }
    27. }
    28. //输出结果
    29. 打电话
    30. 发短信

    原因:两个对象两把锁,不会出现等待的情况,发短信睡了4s,所以先执行打电话

    问题五、六 如果我们把synchronized的方法加上static变成静态方法!那么顺序又是怎么样的呢? (1)我们先来使用一个对象调用两个方法! 答案是:先发短信,后打电话

    (2)如果我们使用两个对象调用两个方法! 答案是:还是先发短信,后打电话

    原因是什么呢? 为什么加了static就始终前面一个对象先执行呢!为什么后面会等待呢? 原因是:对于static静态方法来说,对于整个类Class来说只有一份,对于不同的对象使用的是同一份方 法,相当于这个方法是属于这个类的,如果静态static方法使用synchronized锁定,那么这个 synchronized锁会锁住整个对象!不管多少个对象,对于静态的锁都只有一把锁,谁先拿到这个锁就 先执行,其他的进程都需要等待!

    问题七

    如果我们使用一个静态同步方法、一个同步方法、一个对象调用顺序是什么?

    1. public class dome01 {
    2. public static void main(String[] args) throws InterruptedException {
    3. Phone phone1 = new Phone();
    4. // Phone phone2 = new Phone();
    5. new Thread(() -> {
    6. try {
    7. phone1.sendMs();
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. }).start();
    12. TimeUnit.SECONDS.sleep(1);
    13. new Thread(() -> { phone1.call(); }).start();
    14. }
    15. }
    16. class Phone {
    17. public static synchronized void sendMs() throws InterruptedException {
    18. TimeUnit.SECONDS.sleep(4);
    19. System.out.println("发短信");
    20. }
    21. public synchronized void call() {
    22. System.out.println("打电话");
    23. }
    24. public void hello() {
    25. System.out.println("hello");
    26. }
    27. }
    28. //输出结果
    29. 打电话
    30. 发短信

    原因:因为一个锁的是Class类的模板,一个锁的是对象的调用者。锁住的东西不相同,所以不存在等 待,直接运行。

    问题八

    如果我们使用一个静态同步方法、一个同步方法、两个对象调用顺序是什么?

    1. public class dome01 {
    2. public static void main(String[] args) throws InterruptedException {
    3. Phone phone1 = new Phone();
    4. Phone phone2 = new Phone();
    5. new Thread(() -> {
    6. try {
    7. phone1.sendMs();
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. }).start();
    12. TimeUnit.SECONDS.sleep(1);
    13. new Thread(() -> { phone2.call(); }).start();
    14. }
    15. }
    16. class Phone {
    17. public static synchronized void sendMs() throws InterruptedException {
    18. TimeUnit.SECONDS.sleep(4);
    19. System.out.println("发短信");
    20. }
    21. public synchronized void call() {
    22. System.out.println("打电话");
    23. }
    24. public void hello() {
    25. System.out.println("hello");
    26. }
    27. }
    28. //输出结果
    29. 打电话
    30. 发短信

    原因:两把锁锁的不是同一个东西

    小结

    锁对象:new this 具体的一个手机

    锁class:static Class 唯一的一个模板

    锁住代码块:

    1. synchronized(xxx) {
    2. }

    xxx 可以是 this 或者 Object 或者 xxx.class,下面我们就根据这 3 种不同加锁方式进行展开讨论。

    this

    表示的是锁住当前对象,和原来使用同步实例方式一样,锁住了当前的对象。

    1. public class SynchronizedCodeTest {
    2. public static void main(String[] args) {
    3. SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
    4. for (int i = 0; i < 5; i ++) {
    5. Thread thread = new Thread(() -> {
    6. synchronizedCodeTest.testSynchronizedCode();
    7. });
    8. thread.start();
    9. }
    10. }
    11. int count = 0;
    12. public void testSynchronizedCode() {
    13. System.out.printf("%s-testSynchronizedCode-start-count=%s\n", Thread.currentThread().getName(), count);
    14. synchronized (this) {
    15. System.out.printf("%s-testSynchronizedCode-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
    16. count ++;
    17. System.out.printf("%s-testSynchronizedCode-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
    18. }
    19. System.out.printf("%s-testSynchronizedCode-end-count=%s\n", Thread.currentThread().getName(), count);
    20. }
    21. }

    synchronized(this) 是锁住什么

    1. public class SynchronizedCodeTest {
    2. public static void main(String[] args) {
    3. for (int i = 0; i < 5; i ++) {
    4. SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
    5. Thread thread = new Thread(() -> {
    6. synchronizedCodeTest.testSynchronizedCode();
    7. });
    8. thread.start();
    9. }
    10. }
    11. }

    Object

    同步代码块带来了灵活性,它不再只是锁住当前对象了,可以锁住任何我们创建的对象,下面就来看看。

    1. public class SynchronizedCodeTest {
    2. public static void main(String[] args) {
    3. Object lock = new Object();
    4. SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
    5. for (int i = 0; i < 5; i ++) {
    6. Thread thread = new Thread(() -> {
    7. synchronizedCodeTest.testSynchroniedLock();
    8. });
    9. thread.start();
    10. }
    11. }
    12. int count = 0;
    13. Object lock = null;
    14. public SynchronizedCodeTest(Object lock) {
    15. this.lock = lock;
    16. }
    17. public void testSynchroniedLock() {
    18. System.out.printf("%s-testSynchroniedLock-start-count=%s\n", Thread.currentThread().getName(), count);
    19. synchronized (lock) {
    20. System.out.printf("%s-testSynchroniedLock-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
    21. count ++;
    22. System.out.printf("%s-testSynchroniedLock-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
    23. }
    24. System.out.printf("%s-testSynchroniedLock-end-count=%s\n", Thread.currentThread().getName(), count);
    25. }
    26. }
    1. public static void main(String[] args) {
    2. Object lock = new Object();
    3. for (int i = 0; i < 5; i ++) {
    4. SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
    5. Thread thread = new Thread(() -> {
    6. synchronizedCodeTest.testSynchroniedLock();
    7. });
    8. thread.start();
    9. }
    10. }

    xxx.class

    再来看看最后一种代码块锁 Class 类,这和 public static synchronized testSynchronizedStatic() 的作用是一样的,区别就只是代码块的锁范围可变。我们直接看看代码例子。

    1. public class SynchronizedCodeTest {
    2. public static void main(String[] args) {
    3. for (int i = 0; i < 5; i ++) {
    4. SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
    5. Thread thread = new Thread(() -> {
    6. synchronizedCodeTest.testSynchronizedCodeClass();
    7. });
    8. thread.start();
    9. }
    10. }
    11. int count = 0;
    12. public void testSynchronizedCodeClass() {
    13. System.out.printf("%s-testSynchronizedCodeClass-start-count=%s\n", Thread.currentThread().getName(), count);
    14. synchronized (SynchronizedCodeTest.class) {
    15. System.out.printf("%s-testSynchronizedCodeClass-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
    16. count ++;
    17. System.out.printf("%s-testSynchronizedCodeClass-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
    18. }
    19. System.out.printf("%s-testSynchronizedCodeClass-end-count=%s\n", Thread.currentThread().getName(), count);
    20. }
    21. }

    总结

    这篇介绍了「synchronizd 代码块」的 3 种使用方式,并详细介绍了各自的使用方式和区别。简单的列个表。

    类型使用方式锁作用范围thissynchronized(this){}锁住当前的实例对象objectsynchronized(lock){}锁住其他实例对象,比较灵活xxx.classsynchronized(xxx.class){}锁住 Class 对象

  • 相关阅读:
    2022-11-14对象树模型
    华为开发者大会:全场景智能操作系统HarmonyOS NEXT
    4、Jvm(栈)
    STM32 I2C总线锁死原因及解决方法
    一文了解HTTP协议
    CSS第一节
    基于改进NSGA-Ⅱ算法的开关磁阻电机再生制动优化控制方法
    尚硅谷大数据项目《在线教育之实时数仓》笔记004
    【python】(九)python的模块与包
    vue实战入门后台篇七:springboot+mybatis实现网站后台-各实体界面接口对接
  • 原文地址:https://blog.csdn.net/weixin_42383952/article/details/126413496