• 【Java】线程的同步和互斥锁


    1. 线程的同步

    2. 线程同步机制

    1. 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
    2. 也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。

    3. 同步具体方法–Synchronized

    1. 同步代码块
    synchronized(对象){ //得到对象的锁,才能操作同步代码
    	//需要被同步代码;
    }	
    
    • 1
    • 2
    • 3
    1. synchronized还可以放在方法声明中,表示整个方法为同步方法
    public synchronized void m(String name) {
    	//需要被同步的代码
    }
    
    • 1
    • 2
    • 3
    1. 如何理解:
      就好像某小伙伴上厕所前先把门关上(上锁),完事后再出来(解锁),那么其它小伙伴就可在使用厕所了,如图:
      在这里插入图片描述

    4. 使用synchronized解决售票“超卖”的问题:

    • 使用多线程,模拟三个窗口同时售票100张
    	SellTicket03 sellTicket03 = new SellTicket03();
    	new Thread(sellTicket03).start();//第1个线程-窗口
    	new Thread(sellTicket03).start();//第2个线程-窗口
    	new Thread(sellTicket03).start();//第3个线程-窗口
    
    // 实现接口的方式,使用 synchronized 实现线程同步
    class SellTicket03 implements Runnable {
    
        private int ticketNum = 200;//让多个线程共享 ticketNum
        private boolean loop = true; //控制 run 方法变量
    
        public synchronized void sell() { //同步方法, 在同一时刻, 只能有一个线程来执行sell方法
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                loop = false;
                return;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
                    + " 剩余票数=" + (--ticketNum));
    
        }
    
        @Override
        public void run() {
            while (loop) {
                sell(); // sell方法是一个同步的方法
            }
        }
    }
    
    • 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
    • 可以看到不会出现票 ”超卖“ 的现象了

    在这里插入图片描述

    • 使用 synchronized 同步,可以防止多个线程去操作同一段代码,造成售票“超卖”现象

    5. 分析同步原理

    在这里插入图片描述

    6. 互斥锁

    6.1 基本介绍

    • 上面的锁也称:互斥锁
    1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
    2. 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象
    3. 关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
    4. 同步的局限性:导致程序的执行效率降低
    5. 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
    • 上面的 public void synchronized sell() 方法是属于 非静态的同步方法,这时锁在 this 对象,可以使用 this 表示锁
    • 代码块加锁和方法上加锁都是可以的
    // 代码块实现加锁的方式
    public void sell() { 
    
        synchronized (this) { // 同步代码块
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                loop = false;
                return;
            }
    
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"
                    + " 剩余票数=" + (--ticketNum));
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 非静态的同步方法或代码块的锁也可以是其他对象(要求是同一个对象,多个线程操作同一个对象),如:此时线程操作的对象 object 是同一个对象,所以可以使用 object 来表示锁
    Object object  = new Object();
    
    synchronized (object) {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 同步方法(静态的)的锁为当前类本身
    同步方法(静态的)的锁为当前类本身
    1. public synchronized static void m1() {} 锁是加在当前类 SellTicket03.class
    
    // 静态方法的锁
    public synchronized static void m1() {
    
    }
    
    2. 如果在静态方法中,实现一个同步代码块
    // 代码块的锁
    public static  void m2() {
    	synchronized (SellTicket03.class) {
    	 System.out.println("m2");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    6.2 注意事项和细节

    1. 同步方法如果没有使用static修饰:默认锁对象为this
    2. 如果方法使用static修饰,默认锁对象:当前类.class
    3. 实现的落地步骤:
      • 需要先分析上锁的代码
      • 选择 同步代码块(同步的范围小效率高)或 同步方法
      • 要求多个线程的锁对象为同一个即可
  • 相关阅读:
    【arm实验1】GPIO实验-LED灯的流水亮灭
    Spring Cloud Netflix Ribbon
    Vue之Vue的介绍&安装&开发实例&生命周期钩子
    Uniapp云开发(Uniapp入门)
    关于四元数归一化
    Leetcode 951. 翻转等价二叉树
    【Android】SVGAPlayer的使用介绍
    零经验想跳槽转行网络安全,需要准备什么?
    VS code扩展开发详解
    代码随想录算法训练营第五十一天|309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费
  • 原文地址:https://blog.csdn.net/qq_41684621/article/details/124039521