synchronized监视器锁

news2024/11/25 2:38:55

1、synchronized&监视器锁

1.1 synchronized 介绍

在 Java 中,synchronized 是一种关键字,用于实现线程的同步和互斥控制。它可以修饰方法或代码块,用于保护共享资源的访问,避免多个线程同时修改数据而引发的并发问题。

具体来说,synchronized 关键字的作用如下:

  1. 互斥性:synchronized 关键字确保在同一时刻只有一个线程可以执行被 synchronized 修饰的方法或代码块。当一个线程进入 synchronized 代码块时,它会尝试获取对象的监视器锁(内部锁)。如果该锁已被其他线程占用,则当前线程将被阻塞,直到获取到锁才能执行,从而保证了多个线程对共享资源的互斥访问。

  2. 可见性:synchronized 关键字不仅提供了互斥性,还保证了线程在释放锁之前对共享变量所做的修改对其他线程是可见的。这意味着当一个线程修改了由 synchronized 保护的共享变量后,其他线程在获取锁之后能够看到最新的值,避免了数据不一致的问题。

使用 synchronized 关键字可以有效地防止多个线程同时访问共享资源而导致的数据竞争、竞态条件等并发问题。它是 Java 中最常用的同步机制之一。

在使用 synchronized 时需要注意以下几点:

  1. 能够保护共享资源的代码块应尽量缩小范围,避免不必要的锁竞争。

  2. synchronized 关键字可以用于实例方法、静态方法和代码块。对于实例方法和代码块,锁对象为当前实例;对于静态方法和代码块,锁对象为当前类的 Class 对象。

  3. 如果多个线程访问的是不同的对象实例,那么它们之间的锁是不互斥的。也就是说,synchronized 隔离了不同对象实例之间的并发访问。

  4. 在某些情况下,使用 Lock 接口及其实现类(如 ReentrantLock)可以提供更灵活和精细的锁控制,但需要手动释放锁。

总之,synchronized 关键字是 Java 中用于实现线程同步和互斥控制的重要工具,通过确保临界区的独占性和可见性,有效地保护了共享资源,提高了多线程程序的安全性和正确性。

1.2 监视器锁(Monitor Lock也称为内部锁或互斥锁)

当使用关键字synchronized时,会涉及到锁的概念。对于synchronized代码块或方法,锁是一个用于保护共享资源的机制。

在Java中,每个对象都有一个与之关联的监视器锁(也称为内部锁或互斥锁)。当某个线程希望进入一个被synchronized修饰的代码块或方法时,它必须先获得该对象的锁,才能执行代码块或方法。

Object object2 = new Object();

synchronized (object2) {
    // 同步代码块
    // 这里的锁就是对象object2
    // 只有获得object2的锁才能执行这段同步代码块
}

在上述代码中,使用了一个名为object2的对象作为锁对象。只有获得了object2的锁,即成功获取到object2对象的监视器锁,才能执行synchronized代码块内的内容。其他尝试获取object2锁的线程会被阻塞,直到该锁被释放。

因此,该代码片段中的synchronized (object2)意味着同一时间只有一个线程能够进入与object2关联的synchronized代码块,并且其他线程需要等待锁的释放才能继续执行。

请注意,锁对象可以是任意对象,这里的"object2"只是一个示例变量名。重要的是,在多个线程之间共享同一个锁对象,以实现线程同步和互斥控制。

二、线程安全


2 .1 线程安全演示


多线程共同访问成员变量(共享),并进行修改,那么就会存在线程安全问题。

如下:桌子上有20个豆子,每个线程,在桌子上拿走1个豆子,桌子上显示剩余的豆子数量。

当桌子上剩余的豆子数量为0时,抛出异常。

package day06.threadDemo;
 
public class Demo6 {
    public static void main(String[] args) {
        Table table = new Table();
        Table.Person p1 = table.new Person();
        Table.Person p2 = table.new Person();
        p1.start();
        p2.start();
 
    }
 
}
 
class Table{
    int beans = 20;
    public int getBeans(){
        if(beans ==0){
            throw new RuntimeException("豆没了");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return beans--;
    }
 
    class Person extends Thread{
        @Override
        public void run() {
            while (true){
                int beans = getBeans();
                System.out.println(this.getName()+":"+beans);
            }
        }
    }
}

执行结果:

 当桌子上剩余豆子不为0时,就去桌子上取豆子。

当桌子上还剩1个豆子时,线程0,和线程1,同时去桌子上取豆子(这里面有几率问题,我们开了两个线程,他们不一定同时去取最后一个豆子。如果不同时取,就不会出现线程安全问题,如果正好两个线程同时去取了最后1个),线程0把最后一个豆子取走了,同时线程1也来取,也取走一个,那桌子上就有-1个了。 

2.2 解决线程安全问题


1、能不访问共同变量,就不要访问共同变量,能不修改就不修改,

2、加锁(但是会影响性能)

public synchronized int  getBeans()

给getBeans这个方法加锁。

一个线程进入这个方法之后,别的线程就进不来了,该线程执行完这个程序后,释放锁,别的线程就可以使用这个程序(举个恶心的例子,多个人上一个厕所,进去一个人后,其他的人就要排队了)

2.3 方法加锁


在方法中添加关键字synchronized

    public synchronized int  getBeans(){
        if(beans ==0){
            throw new RuntimeException("豆没了");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return beans--;
    }


等价于

    public int getBean(){
        synchronized (this){
            if(beans ==0){
                throw new RuntimeException("豆没了");
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return beans--;
        }
        }

 synchronized () 中,要执行资源(具体哪个对象的锁),this就是自身的对象。
给整个方法加锁。

整体代码:

这里是以自身Table对象作为锁的资源。

package com.example.analyzestack.controller;



public class SynchronizedTest {
    public static void main(String[] args) {
        Table table = new Table();
        Table.Person p1= table.new Person();
        Table.Person p2 = table.new Person();
        p1.start();
        p2.start();
    }

}


class Table{
    int beans =20;
    public synchronized int getBeans() {
        // synchronized  加锁
        if(beans==0){
            throw new RuntimeException("豆子没有了");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return beans--;
    }

    class Person extends Thread{
        @Override
        public void run() {
            while (true){
                int beans =getBeans();
                System.out.println(this.getName()+":"+beans);
            }
        }
    }
}

2.4 静态方法加锁


静态方法,默认使用的是类的class当锁

    // 静态方法,默认使用的是类的class当锁(Table.class)
    public static synchronized void test1(){
        synchronized (Table.class){
            
        }
    }


2.5 无效加锁


我们加锁,是对同一个对象加锁。像这种情况

    public synchronized int  getBeans(){
        Object obj = new Object();
        synchronized (obj){
            if(beans ==0){
                throw new RuntimeException("豆没了");
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return beans--;
 
        }


给obj加锁,obj不是我们这个类的对象,每次调用getBeans方法都会创建一个obj。这个锁是不管用的。

2.6 死锁


两个线程,线程1占用锁A,去拿锁B。线程2占用锁B,去拿锁A。相互拿不到资源,造成死锁。

举个例子

package day06.threadDemo;
 
public class Demo8 {
    public static void main(String[] args) {
        Boo boo = new Boo();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                boo.test1();
            }
        };
 
        Thread t2 = new Thread(){
            @Override
            public void run() {
                boo.test2();
            }
        };
        t1.start();
        t2.start();
 
    }
}
 
class Boo{
    private Object object1 = new Object();
    private Object object2 = new Object();
 
    public void test1(){
        synchronized (object1){
            System.out.println(Thread.currentThread().getName()+"-obj1");
            synchronized (object2){
                System.out.println(Thread.currentThread().getName()+"-obj2");
            }
        }
    }
 
    public void test2(){
        synchronized (object2){
            System.out.println(Thread.currentThread().getName()+"-obj2");
            synchronized (object1){
                System.out.println(Thread.currentThread().getName()+"-obj1");
            }
        }
    }
}

运行这个程序,程序一直执行不完。此时,就已经死锁了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python重写父类__init__方法的必要性与实现方法

Python重写父类__init__方法的必要性与实现方法 在Python中,一个类可以继承自另一个类,从而获得另一个类的属性和方法。当我们继承一个父类时,通常我们需要重写其中的一些方法,以满足我们自己的需求。在这篇文章中,我…

玩机搞机-----带你了解高通刷机平台中的一些选项释义 玩转平台

很多刷机工具玩家都使用过,但对于一些新手来说。有些选项所表达的意义不太了解,选择与否严重会导致机型固件刷完个别功能出现故障,今天的这个博文对有些刷机平台中的选项做个简单的说明。 一 小米刷机平台 MiFlash.截止目前最新的版本是2022…

最新|2024年QS世界大学排名前100榜单发布

6月28日世界高等教育研究机构Quacquarelli Symonds(QS)率先公布了2024年世界大学排名,本次QS排名因指标和权重的重大调整,导致排名发生较大变化。知识人网小编将新的评分标准及前100的大学榜单整理如下,供读者参考。 前…

Unity渲染工程收集

NPR 非真实渲染 UnityURP-AnimeStyleCelShader SSR 屏幕空间反射 UnitySSReflectionURP

消息传输不丢失:掌握消息中间件的持久化机制

当涉及到消息的持久化和重放时,我们可以使用Spring Boot与RabbitMQ来实现这个场景。RabbitMQ支持消息的持久化,以确保在发送和接收过程中消息不会丢失。同时,我们可以使用消息的重放机制,以便在需要时重新发送消息。 首先&#xf…

leetcode:387. 字符串中的第一个唯一字符(python3解法)

难度:简单 给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。 示例 1: 输入: s "leetcode" 输出: 0示例 2: 输入: s "loveleetcode" 输出: 2示例 3: 输…

1253. 重构 2 行二进制矩阵(力扣)

1253. 重构 2 行二进制矩阵(力扣) 题目第一种方式分析测试代码运行结果 第二种方式测试代码运行结果 题目 给你一个 2 行 n 列的二进制数组: 矩阵是一个二进制矩阵,这意味着矩阵中的每个元素不是 0 就是 1。 第 0 行的元素之和为…

系统架构设计师-软件工程(1)

一、软件过程模型 (1)瀑布模型、(2)V模型【瀑布变种】、(3)原型模型、 (4)螺旋模型【原型瀑布】、(5)构件组装模型/基于构件的开发方法、 (…

学习Kotlin~类

类 类的field 类定义的每一个属性,kotlin都会产生一个filed,一个setter(),一个getter()field用来存储属性数据,不能直接定义,kotlin会封装,保护它里面数据,只暴露给getter和setter使用只有可变属性才有setter方法需要…

UNITY3D弹幕游戏,万人同屏解决方案_类萌宠宠之战

先上效果 (类萌宠宠之战)弹幕游戏,万人同屏解决方案演示 UNITY默认的人物动画显示方案是 SkinnedMeshRenderer 该动画的计算是由CPU计算(计算骨骼位置所影响的顶点位置) 所以是CPU计算,物体大于2000个时…

Python3,掌握这几种并行处理,轻轻松松提升for循环速度。

并行处理几种方法 1、引言2、并行处理2.1 定义2.2 并行处理优缺点2.3 并行处理的常用库2.4 代码示例2.4.1 multiprocessing2.4.2 concurrent.futures2.4.3 joblib2.4.4 threading 3、总结 1、引言 小屌丝:鱼哥,你给我讲一讲并行处理呗。 小鱼&#xff1…

Android:datePicker对话框的使用

一、前言&#xff1a;这篇文章是关于DatePickerDialog&#xff0c;点击按钮出现一个日期选择器对话框&#xff0c;通过点击确认把选则的日期显示到文本控件上。 二、上代码 页面布局xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout…

正则表达式回溯引发的生产惨案

文章目录 背景问题原因分析如何解决&#xff1f;chatgpt 3.5GP4的表现未完待续 背景 业务上的一个字段在解析时为了避免脏数据导致后续ETL的异常&#xff0c;决定从源头将该字段严格按照设计的规则去匹配。该字段的上传是设备端传上来的文件中的一个字段。 正向&#xff1f;反…

「C/C++」C++类与类的关系(UML类图+代码说明)

✨博客主页&#xff1a;何曾参静谧的博客 &#x1f4cc;文章专栏&#xff1a;「C/C」C/C学习 相关术语 &#x1f3af;依赖关系&#xff08;Dependency&#xff09;&#xff1a;表示一个类的实现需要另一个类的协助&#xff0c;虚线箭头&#xff0c;箭头指向被依赖的类。 &#…

基于当量因子法、InVEST、SolVES模型等多技术融合在生态系统服务功能社会价值评估中的应用及论文写作、拓展分析

生态系统服务是人类从自然界中获得的直接或间接惠益&#xff0c;可分为供给服务、文化服务、调节服务和支持服务4类&#xff0c;对提升人类福祉具有重大意义&#xff0c;且被视为连接社会与生态系统的桥梁。自从启动千年生态系统评估项目&#xff08;Millennium Ecosystem Asse…

2023软科中国大学智能建造专业排名结果出炉(共54所高校)

智能建造专业&#xff08;Intelligent Construction&#xff09;是一个新兴的跨学科领域&#xff0c;它涉及到建筑、土木工程、计算机科学、数据科学等多个学科的知识。智能建造专业专注于研究如何利用先进的计算机技术、人工智能&#xff08;AI&#xff09;、物联网&#xff0…

进销存软件市场成熟,为什么还要用低代码构建?

关键词&#xff1a;进销存软件、群晖NAS、低代码平台 编者按&#xff1a; 进销存管理软件产业已经逐步走向成熟&#xff0c;产品种类也越来越丰富&#xff0c;也正因如此&#xff0c;企业在选择购买进销存软件的时候&#xff0c;往往不知该如何选择。不少的企业会直接选择平台型…

[golang 微服务] 8.go-micro的负载均衡操作,go Web框(Gin,Beego)调用go-micro微服务

一.先创建go-micro服务端 启动consul 需要先启动consul, consol相关内容见 [golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群 [golang 微服务] 6. GRPC微服务集群Consul集群grpc-consul-resolver案例演示 启动consul命令,这里,使用dev模式: consul agen…

手机上怎么压缩视频?教你几招手机压缩视频小技巧

压缩视频是一种有益的技术&#xff0c;可以帮助人们在存储、传输和观看视频时更有效率和便捷。尤其是在现今数字化信息时代&#xff0c;视频已经成为人们日常生活中不可或缺的一部分&#xff0c;因此更需要使用视频压缩技术来更好地管理和使用这些视频文件。下面给大家分享几种…

Yolov8优化: 多分支卷积模块RFB,扩大感受野提升小目标检测精度

1.RFB-Net介绍 论文&#xff1a;https://arxiv.org/pdf/1711.07767.pdf 代码&#xff1a;GitHub - GOATmessi7/RFBNet: Receptive Field Block Net for Accurate and Fast Object Detection, ECCV 2018 受启发于人类视觉的Receptive Fields结构&#xff0c;本文提出RFB&#xf…