• Java 多线程之 synchronized (互拆锁/排他锁/非观锁)


    一、概述

    • 在Java中,synchronized 关键字用于实现线程之间的同步。提供了一种简单而强大的机制来控制多个线程之间的并发访问,确保共享资源的安全性和一致性。它解决了多线程环境中的竞态条件、数据竞争和内存模型等问题,是实现线程安全的重要手段之一。它主要有以下几个作用:

      1. 互斥性(Mutual Exclusion):synchronized 用于实现互斥访问,确保同一时间只有一个线程可以进入被 synchronized 修饰的代码块或方法。当一个线程获取了锁(也称为监视器锁)后,其他线程就无法进入该代码块或方法,直到锁被释放。
      2. 可见性(Visibility):synchronized 不仅保证了互斥性,还保证了对共享变量的修改对其他线程是可见的。当一个线程释放锁时,它会将对共享变量的修改刷新到主内存,而其他线程在获取锁之前会从主内存中重新读取共享变量的值,确保了线程之间的可见性。相应的 volatile 关键字也有这个功能,请看 volatile 的使用说明
      3. 有序性(Ordering):synchronized 保证了代码的执行顺序按照线程的获取锁的顺序来进行。即使在多个线程之间存在指令重排序,通过 synchronized 的释放和获取锁操作,可以确保代码块内的操作按照顺序执行。
      4. 内存屏障(Memory Barriers):synchronized 的进入和退出操作都会插入内存屏障,这些屏障会阻止指令重排序和确保内存的可见性。这种特性使得 synchronized 不仅仅是一种同步机制,还可以作为一种内存屏障来确保指令的有序执行。
      5. 可重入性:可重入性是由内置锁(synchronized)和可重入锁(ReentrantLock)实现的。当一个线程已经获得了一个锁,并且在持有锁的代码块或方法中再次请求同一个锁时,它可以直接通过,而不会被阻塞。这样的机制称为可重入锁(Reentrant Locking)或递归锁(Recursive Locking)。
    • synchronized 是 Java 中用于实现内置锁(Intrinsic Lock)或监视器锁(Monitor Lock)的关键字,它属于独占锁(Exclusive Lock)或互斥锁(Mutual Exclusion Lock)。

    • 使用时有以下几点注意

      • synchronized 锁的是对象。
      • 不建议使用String、Integer、Long等常量作为锁的对象。因这样的锁是全局的,如果多个线程中使用了相同的锁,会导致全部阻塞。
      • 属于升级锁,由无锁、轻量级锁(偏向锁、自旋锁)到重量级锁根据情况自动升级。
      • synchronized 可以修饰方法,也可以修饰代码块。

    二、使用方法

    • 作用在代码上,相当于给代码块加锁(Lock)

        	public void performTask() {
              // synchronized 作用于代码块
          	synchronized (lock) {
            		// 业务逻辑,同步代码块,对共享资源进行操作
          	}
        	}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 作用在方法上,相当于给整个方法加锁(Lock)

          // synchronized 作用在方法上
        	public synchronized void increment() {
              // 业务逻辑,同步代码块,对共享资源进行操作
        	}
      
      • 1
      • 2
      • 3
      • 4

    三、测试示例

    • 一个会出异常的示例

      • 在下面这个测试示例中有一个 Counter 类,在这个类中有一个 add 方法,当记数 count 小于 50000 时自增。然后在 main 方法中启动100个线程来同时进行增加操作,由于没有加锁(synchronized),最后结果总是会大于 50000。
      package top.yiqifu.study.p004_thread;
      
      import java.io.File;
      import java.util.ArrayList;
      import java.util.List;
      
      public class Test061_ThreadSynchronized {
          public static class Counter {
              private volatile int count = 0;
      
              public void increment() {
                  count++;
              }
      
              public int getCount() {
                  return count;
              }
      
              public void add(){
                  if(this.getCount() < 50000){
                      // Thread.yield();
                      File.listRoots();// 模拟复杂业务,执行一些额外的语句
                      this.increment();
                  }
              }
          }
      
          public static void main(String[] args) {
              Counter counter = new Counter();
      
              List<Thread> threads = new ArrayList<>();
              for(int count = 0; count < 100; count++) {
                  Thread thread = new Thread(() -> {
                      for (int i = 0; i < 1000; i++) {
                          counter.add();
                      }
                  });
                  threads.add(thread);
              }
              for(Thread t : threads) {
                  t.start();
              }
      
              for(Thread t : threads) {
                  try {
                      t.join();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
      
              System.out.println("最后结果: " + counter.getCount());
          }
      
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
    • 修改正示例

      • 要解决这个问题,可以使用 synchronized 关键字来对代码块加锁。

      • 在代码块上加 synchronized 关键字

                public void add(){
                    synchronized(this) {
                        if(this.getCount() < 50000){
                            File.listRoots();// 模拟复杂业务,执行一些额外的语句
                            this.increment();
                        }
                    }
                }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
      • 在 add 方法上加 synchronized 关键字

                public synchronized  void add(){
                    if(this.getCount() < 50000){
                        File.listRoots();// 模拟复杂业务,执行一些额外的语句
                        this.increment();
                    }
                }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
  • 相关阅读:
    C语言--贪吃蛇
    数据结构——线性表
    【PHP】单例模式
    Godot拉伸设置
    JavaScript面向对象学习深拷贝、浅拷贝(三)
    【多线程案例】Java实现简单定时器(Timer)
    Spring Cloud Gateway 不小心换了个 Web 容器就不能用了,我 TM 人傻了
    Windows下 开机自启动jar包
    Java毕业设计-基于springboot开发的网上租赁系统设计与实现-毕业论文(附毕设源代码)
    2022最新大厂面试题整合,阿里、华为、美团等高工手打Java面试宝典(全彩图文版)全网首次公开
  • 原文地址:https://blog.csdn.net/qifu123/article/details/134558825