Java中的 锁现象演示和原理解释 多线程操作资源类 八种案例 同步方法 静态方法 对象锁 类锁

目录

面试题

案例 1 标准访问有 ab 两个线程

案例 2 其中一个同步方法暂停 3 秒

案例 3 新增普通方法

案例 4 创建两个对象

案例 5 两个静态同步方法 一个对象

案例 6 两个静态同步方法 两个对象

案例 7 一个静态同步方法 一个普通同步方法 一个对象

案例 8 一个静态同步方法 一个普通同步方法 两个对象

思考总结

notify 方法


面试题

案例 1 标准访问有 ab 两个线程

在主线程里面暂停几秒钟的线程

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public synchronized void sendEmail(){
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
}
public class LockDemo1 {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.sendSMS();
        },"b").start();

    }
}

打印输出

案例 2 其中一个同步方法暂停 3 秒

在资源类的方法里面暂停几秒钟

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
}
public class LockDemo1 {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.sendSMS();
        },"b").start();

    }
}

sendEmail()sendSMS() 都是使用 synchronized 修饰的,这意味着它们在同一时刻只能有一个线程能进入执行。加锁是基于 phone 类的实例的,也就是说,如果有一个线程在执行某个同步方法,其他线程就会被阻塞,直到该线程执行完成并释放锁。

  • 当第一个线程(线程 a)执行 phone.sendEmail() 方法时,它会获取 phone 对象的锁,并且由于该方法包含 TimeUnit.SECONDS.sleep(2),它会在执行时暂停 2 秒。
  • sendEmail 方法执行期间,第二个线程(线程 b)尝试执行 phone.sendSMS(),但是由于 sendSMS 也是一个同步方法,且它也是基于 phone 对象的锁,所以它会被阻塞,直到线程 a 执行完成并释放锁。

案例 3 新增普通方法

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.hello();
            phone.sendSMS();
        },"b").start();

    }
}

案例 4 创建两个对象

对象锁

一个对象一把锁

两个对象两把锁

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类
        phone phone2 = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
//            phone.hello();
//            phone.sendSMS();
            phone2.sendSMS();
        },"b").start();

    }
}

案例 5 两个静态同步方法 一个对象

静态关键字

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public static synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public static synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.sendSMS();
        },"b").start();

    }
}

当你在 sendEmail()sendSMS() 方法上添加了 static synchronized 关键字后,锁的行为发生了变化。原来这些方法是基于实例对象 (phone 类的实例) 的锁,而加了 static 之后,这些方法变成了基于类锁(类对象锁)。

关键点:

  1. 非静态同步方法:如果没有 static,同步方法会使用实例对象的锁(即 phone 类的实例锁),每个对象都有自己的锁。
    • 在这种情况下,两个线程在同一个 phone 实例上调用 sendEmail()sendSMS() 时,锁是独立的,因为每个对象的锁是分开的。
  1. 静态同步方法:当你使用 static synchronized 时,锁就变成了类级别的锁。这意味着所有类的实例(无论创建多少个实例)都会共享同一把锁,而这把锁是与 phone 类相关的,而不是某个具体实例对象。
    • 也就是说,sendEmail()sendSMS() 不再是基于单个对象 phone 的锁,而是基于 phone 类的类锁。

对你的代码的影响:

  • 静态同步方法sendEmail()sendSMS() 都是静态同步方法,这意味着它们都使用 phone.class 作为锁。
    • 当线程 a 执行 phone.sendEmail() 时,它会获取 phone.class 锁,阻塞其他线程执行任何静态同步方法,直到 sendEmail() 完成。
    • 当线程 b 执行 phone.sendSMS() 时,它也需要获取 phone.class 锁,但因为线程 a 正在执行 sendEmail(),它会被阻塞直到 sendEmail() 执行完成。
  • 非静态方法hello() 方法没有 synchronizedstatic,因此它不是同步的,线程可以自由地调用它,且不会与静态同步方法互相影响。

锁的流程:

  1. 线程 a 调用 sendEmail(),获取 phone.class 锁,开始执行,执行期间睡眠 3 秒。
  2. 线程 b 调用 sendSMS(),但是由于 sendEmail() 还在执行,sendSMS() 会被阻塞,直到线程 a 执行完 sendEmail() 并释放 phone.class 锁。

总结:

当你在方法上加了 static synchronized 关键字时,锁是基于类锁(phone.class),而不是基于对象锁(phone 实例)。这意味着所有线程都必须获取类级别的锁才能进入这两个方法。所以,即使你创建了多个 phone 类的实例,静态同步方法依然会使用同一个类锁。

案例 6 两个静态同步方法 两个对象

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public static synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public static synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类
        phone phone2 = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.sendSMS();
        },"b").start();

    }
}

案例 7 一个静态同步方法 一个普通同步方法 一个对象

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public static synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类
        phone phone2 = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.sendSMS();
        },"b").start();

    }
}

案例 8 一个静态同步方法 一个普通同步方法 两个对象

import java.util.concurrent.TimeUnit;

// 资源类
class phone{
    public static synchronized void sendEmail(){
        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("---email---");
    }
    public synchronized void sendSMS(){
        System.out.println("---sms---");
    }
    public void hello(){
        System.out.println("---hello---");
    }

}
public class LockDemo {
    public static void main(String[] args) { // 主线程
        phone phone = new phone(); // 资源类
        phone phone2 = new phone(); // 资源类

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        // 暂停几秒钟的线程
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.sendSMS();
        },"b").start();

    }
}

思考总结

我们要明白 sync 锁定的是什么

阿里开发手册

能用对象锁 就不要用类锁

synchronized 不是锁方法

是锁资源类

同步方法资源类是对象

同步静态方法资源类是当前类

同步方法锁的是当前对象

同一时间只能允许一个 线程进来同步方法

不允许同时

普通的同步方法锁的是对象锁 对象.class

静态的同步方法锁的是模版 类锁 类.class

能用对象锁 就不要用类锁

notify 方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/956040.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTML应用指南:利用GET请求获取全国特斯拉充电桩位置

随着电动汽车的普及,充电基础设施的建设变得至关重要。作为电动汽车领域的先驱,特斯拉不仅在车辆技术创新上持续领先,还积极构建广泛的充电网络,以支持其不断增长的用户群体。为了提升用户体验和服务质量,开发人员和数…

cuda + cudnn安装

1.安装CUDA Toolkit 在设备管理器(此电脑–右键–属性)的显示适配器中可以查看自己的显卡型号,去下载对应的CUDA Toolkit 。或者输入以下命令查看Driver Version ,cuda Version:12.2代表12.2版本以下兼容可以进行安装 …

【k8s面试题2025】1、练气期

主要通过呼吸吐纳等方法,将外界的天地灵气吸入体内,初步改造身体,使身体素质远超常人。 文章目录 docker 和虚拟机的不同Kubernetes 和 docker 的关系Kube-proxy IPVS 和 iptables 的异同蓝绿发布Kubernetes中常见的数据持久化方式关于 Docke…

vue | 插值表达式

Vue 是一个用于 构建用户界面 的 渐进式 框架 1. 构建用户界面:基于 数据 动态 渲染 页面 2. 渐进式:循序渐进的学习 3. 框架:一套完整的项目解决方案,提升开发效率↑ (理解记忆规则) 插值表达式: 插值表达式是一种 Vu…

【蜂巢——方向,数学】

题目 代码 #include <bits/stdc.h> using namespace std; using ll long long; int dx[6] {-1, -1, 0, 1, 1, 0}; int dy[6] {0, 1, 1, 0, -1, -1}; void cal(int d, int p, int q, int& x, int& y) {x p * dx[d];y p * dy[d];d (d 2) % 6;x q * dx[d];…

C语言进阶习题【1】指针和数组(1)——一维数组

1. 数组名的意义&#xff1a; sizeof(数组名)&#xff0c;这里的数组名表示整个数组&#xff0c;计算的是整个数组的大小。&数组名&#xff0c;这里的数组名表示整个数组&#xff0c;取出的是整个数组的地址。除此之外所有的数组名都表示首元素的地址。&#xff08;一维数…

禁用输入法的方案

车间运行的SF系统&#xff0c;扫描产品条码。WB输入法等工具流氓软件&#xff0c;文本框获得焦点之后&#xff0c;自动打开输入状态&#xff0c;美其名日&#xff0c;智能化。输入法open状态下&#xff0c;扫描条码是乱码的&#xff0c;或是不全&#xff0c;缺字符。分析是输入…

Linux提权-02 sudo提权

文章目录 1. sudo 提权原理1.1 原理1.2 sudo文件配置 2. 提权利用方式2.1 sudo权限分配不当2.2 sudo脚本篡改2.3 sudo脚本参数利用2.4 sudo绕过路径执行2.5 sudo LD_PRELOAD环境变量2.6 sudo caching2.7 sudo令牌进程注入 3. 参考 1. sudo 提权原理 1.1 原理 sudo是一个用于在…

3. 后端验证前端Token

书接上回&#xff0c;后端将token返回给前端&#xff0c;前端存入cookie&#xff0c;每次前端给后端发送请求&#xff0c;后端是如何验证的。 若依是用过滤器来实现对请求的验证&#xff0c;过滤器的简单理解是每次发送请求的时候先发送给过滤器执行逻辑判断以及处理&#xff0…

【转】厚植根基,同启新程!一文回顾 2024 OpenHarmony 社区年度工作会议精彩瞬间

在数字化浪潮奔腾不息的今天&#xff0c;开源技术已成为推动科技创新与产业发展的强大引擎。2025年1月10日-11日&#xff0c;OpenAtom OpenHarmony&#xff08;开放原子开源鸿蒙&#xff0c;以下简称“OpenHarmony”或“开源鸿蒙”&#xff09;社区2024年度工作会议于深圳盛大启…

数据结构(Java版)第九期:LinkedList与链表(四)

专栏&#xff1a;数据结构(Java版) 个人主页&#xff1a;手握风云 目录 一、LinkedList的模拟实现 1.1. 头插法 1.2. 尾插法 1.3. 插入中间节点 1.4. 删除某个节点 1.5. 删除所有为key的元素 二、LinkedList的使用 2.1. 什么是LinkedList 2.2. LinkedList的使⽤ 三、…

第22篇 基于ARM A9处理器用汇编语言实现中断<四>

Q&#xff1a;怎样编写ARM A9处理器汇编语言代码配置使用按键和定时器中断&#xff1f; A&#xff1a;本次实验同样为中断模式和监督模式都设置ARM A9堆栈指针&#xff0c;并使能中断&#xff0c;此外在主程序中调用子程序CONFIG_HPS_TIMER和CONFIG_KEYS分别对HPS Timer 0&…

Python学习(十三)什么是模块、模块的引入、自定义模块、常见的内置模块(math、random、os、sys、uuid、时间模块、加密模块)

目录 一、什么是模块&#xff1f;1.1 定义1.2 分类1.3 五种模块引入的方法1&#xff09;import 模块名&#xff08;全部引入&#xff09;2&#xff09;from 模块名 import 功能名&#xff08;部分引入&#xff09;3&#xff09;from 模块名 import *&#xff08;引入公共功能&a…

宝塔php7.4安装报错,无法安装,php8以上可以安装,以下的不行,gd库什么的都正常

宝塔的依赖问题导致的问题&#xff0c;最后手动挂载后才解决。。。废了三天三夜终于搞好了。。。。无语&#xff5e; 建议&#xff1a;不要一直升级宝塔版本&#xff0c;升级前备份或者开服务商的实例镜像&#xff0c;方便恢复&#xff0c;不然&#xff0c;可就GG了&#xff5…

C语言:-三子棋游戏代码:分支-循环-数组-函数集合

思路分析&#xff1a; 1、写菜单 2、菜单之后进入游戏的操作 3、写函数 实现游戏 3.1、初始化棋盘函数&#xff0c;使数组元素都为空格 3.2、打印棋盘 棋盘的大概样子 3.3、玩家出棋 3.3.1、限制玩家要下的坐标位置 3.3.2、判断玩家要下的位置是否由棋子 3.4、电脑出棋 3.4.1、…

flutter的web页面

有几个服务器 有几个后台 直接通过web端进去虽然说很方便&#xff0c;但… 于是把web页面镶嵌到应用里面去&#xff0c; 这样就换了个方式打开web页面了 比如这里有有个列表 这里是写死了&#xff0c;活的列表可以通过io去获取 import package:flutter/material.dart; imp…

K8S 亲和性与反亲和性 深度好文

今天我们来实验 pod 亲和性。官网描述如下&#xff1a; 假设有如下三个节点的 K8S 集群&#xff1a; k8s31master 是控制节点 k8s31node1、k8s31node2 是工作节点 容器运行时是 containerd 一、镜像准备 1.1、镜像拉取 docker pull tomcat:8.5-jre8-alpine docker pull nginx…

解决conda create速度过慢的问题

问题 构建了docker容器 想在容器中创建conda环境&#xff0c;但是conda create的时候速度一直很慢 解决办法 宿主机安装的是anaconda 能正常conda create,容器里安装的是miniforge conda create的时候速度一直很慢&#xff0c;因为容器和宿主机共享网络了&#xff0c;宿主机…

Banana Pi BPI-RV2 RISC-V路由开发板采用矽昌通信SF2H8898芯片

Banana Pi BPI-RV2 开源网关是⼀款基于矽昌SF2H8898 SoC的设备&#xff0c;1 2.5 G WAN⽹络接⼝、5 个千兆LAN ⽹络接⼝、板载 512MB DDR3 内存 、128 MiB NAND、16 MiB NOR、M.2接⼝&#xff0c;MINI PCIE和USB 2.0接⼝等。 Banana Pi BPI-RV2 开源网关是矽昌和⾹蕉派开源社…

C -- 大端对齐 小端对齐 的人性化解释

网上很多资料对大小端对齐的解释 很多 很全 很准 但为啥老是记不住呢&#xff0c;所有的知识都是基于人性的情感原理&#xff0c;或是世界基本的运行规律而产生的&#xff0c;如果不能把知识和这两点打通&#xff0c;即使记住也很容易忘记。本篇文章基于这两点 分析下大小端对齐…