[JAVA] 什么是Java线程同步机制?

news2024/9/20 8:07:41

          在单线程程序中,每次只能做一件事情,后面的事情需要等待前面的事情完成后才可以进行,如果使用多线程程序,就会发生两个线程抢占资源的问题,所以在多线程编程中,需要防止这些资源访问的冲突,Java提供线程同步的机制来防止资源访问的冲突。

如何理解同步机制?

       举一个生活中的例子,比如一些景点,表演婚俗中抛绣球活动,这个绣球被抛出去以后,下面所有的游客都会去争抢这唯一的绣球,谁抢到绣球就是这个幸运的人,迎娶新娘。

       我们能够发现,无论多少人在下面等待抢绣球,但最终,只有一个人能拿到这个绣球,成为唯一的幸运儿。在这个例子中,就是一个典型的同步机制。

1.同步块

在Java中提供了同步机制,可以有效地防止资源冲突,同步机制使用synchronized关键字。

synchronized(同步锁)关键字的作用就是利用一个特定的对象设置一个锁lock(绣球),在多线程(游客)并发访问的时候,同时允许一个线程(游客)可以获得这个锁,执行特定的代码(迎娶新娘)。执行后释放锁,继续由其他线程争抢

代码演示:

public class SyncSample {
    class Printer{
        public void print(){
            try {
                Thread.sleep(500);
                 System.out.print("魑");
                 Thread.sleep(500);
                 System.out.print("魅");
                 Thread.sleep(500);
                 System.out.print("魍");
                 Thread.sleep(500);
                 System.out.print("魉");
                 System.out.println( );
            }catch (Exception e){
                e.printStackTrace();
            }

        }

       在SyncSample外部类中创建内部类Printer,在print()方法体中,利用Thread.sleep(500)写入一个线程沉睡半秒输出的语句,使代码打印出有一个停顿的效果,作为sleep会抛出异常,我们用try—catch来捕获

       再次增加内部类PrintTesk ,Runnable作为接口,在多线程编程中,Runnable接口通常与线程结合使用,用于创建新的线程来执行特定的任务。

       可以通过将Runnable对象传递给Thread类的构造函数来创建一个新的线程,并在该线程中执行Runnable任务

 class PrintTask implements Runnable{
        public Printer printer;   //定义类的成员变量

        @Override
        public void run() {      //Runnable接口的核心方法run()
            printer.print();     //调用printer对象的print方法,执行输出“魑魅魍魉”操作
        }
    }

再继续加入一个start方法,用于创建线程后实现输出

 public void start() {
        Printer printer = new Printer();//创建Printer类的实例
        for (int i = 0; i < 10; i++) {         //循环创建线程
            PrintTask tack = new PrintTask(); //在每次循环中,创建一个PrintTask对象tack,并将之前创建的printer对象赋值给tack的printer成员变量
            tack.printer = printer;
            Thread thread = new Thread(tack);    //创建一个新的Thread对象,将tack作为参数传入构造参数即这个线程会执行tack中的run方法
            thread.start();         //开始线程执行
        }
    }

最后在主函数调用

 public static void main(String[] args) {
        SyncSample syncSample = new SyncSample();
        syncSample.start();
    }

运动结果 

        我们发现,再多线程程序运行的时候,十个线程底层都是引用了同一个printer对象, 这会使十个线程同时执行,同时对魑进行输出,然后又会对魅进行输出,接着魍魉进行一个个输出,最后得到以上无序混乱的结果。

       在多线程情况下,所有线程都在争抢同一个资源,也就是当前printer对象时,使得我们打印出的数据并不是完整合适的,出现混乱的情况。

      为了解决这样的问题,我们需要引入锁的机制,让线程一个个排队来执行,对于打印输出的代码,如果前面的线程没有执行完毕,后面所有的线程就一直处于阻塞等待的状态,直到拥有获取这个线程的执行权力

class Printer {
        Object lock =new Object();

        public void print() {
         synchronized (lock) {
             try {
                 Thread.sleep(500);
                 System.out.print("魑");
                 Thread.sleep(500);
                 System.out.print("魅");
                 Thread.sleep(500);
                 System.out.print("魍");
                 Thread.sleep(500);
                 System.out.print("魉");
                 System.out.println( );

             } catch (Exception e) {
                 e.printStackTrace();
             }
         }

Object lock = new Object();

在Printer内部类中加入了一个用于同步的对象锁,这个对象可以被多个线程用来进行同步操作,确保在同一时间只有一个线程能够访问被synchronized关键字修饰的代码块。

再次运行:

在程序运行时由Java来控制锁对象的分配,哪个线程优先的获取到了锁,哪个线程就可以执行里面的代码,这段代码执行完成后,锁会被释放,剩余的其他线程会争抢这把锁,之后按照相同的规则,抢夺—释放—抢夺,直到所有线程完成处理,程序结束。

关于synchronized的锁对象,synchronized用于实现线程同步的机制

  •  synchronized代码块 -任意对象即可
  •  synchronized方法 -this当前对象
  •  synchronized静态方法 - 该类的字节码对象

1.synchronized代码块 -任意对象即可

同以上案例代码一样,内部类中新增锁对象lock,在代码中为了保证线程的同步,使用synchronized()关键字,括号中增加锁对象lock,这样在多线程运行过程中,哪个线程先争抢到这个锁,它就拥有代码的执行权,其他线程处于阻塞状态

 class Printer {
        Object lock =new Object();   //定义锁对象

        public void print() {

         synchronized (lock) {

             try {
               
               ...............
               ...............

             } catch (Exception e) {

                 e.printStackTrace();
             }
         }

  2.关于synchronized方法 -this当前对象

 public synchronized void print2  () {  //synchronized直接放在代码声明部分
                try {
                    Thread.sleep(500);
                    System.out.print("魑");
                    Thread.sleep(500);
                    System.out.print("魅");
                    Thread.sleep(500);
                    System.out.print("魍");
                    Thread.sleep(500);
                    System.out.print("魉");
                    System.out.println( );

                } catch (Exception e) {
                    e.printStackTrace();
                
            }

       此时synchronized不在代码中进行书写,而是把synchronized关键字放在声明部分 ,它是写在方法上进行描述,当一个方法被声明为synchronized时,多个线程在访问这个方法时会进行同步,既同一时间只有一个线程能够执行这个方法

       synchronized方法使用的是对象锁,锁的对象是当前实例(this),表示一个线程进入一个sy实例方法后,它会获取当前对象的锁,其他线程如果想进入同一个对象这个synchronized方法,就必须等待当前线程释放这个锁

 3.synchronized静态方法 - 该类的字节码对象

在Java中,当一个方法被声明为synchronized static(同步静态方法)时,它使用的锁是该类的字节码对象。

锁的对像

     对于静态方法,锁的对象是类的class对象(该类的字节码对象),这意味着不同的对象实例在调用同一个类的synchronized static方法时,也会进行同步

public class SyncSample {
    static class Printer {     //添加静态方法
        Object lock =new Object();

        public void print1() {
         synchronized (lock) {
             try {
                 Thread.sleep(500);
                 System.out.print("魑");
                 Thread.sleep(500);
                 System.out.print("魅");
                 Thread.sleep(500);
                 System.out.print("魍");
                 Thread.sleep(500);
                 System.out.print("魉");
                 System.out.println( );

             } catch (Exception e) {
                 e.printStackTrace();
             }
         }

        }
 public static synchronized void print2  () { 
                try {
                    Thread.sleep(500);
                    System.out.print("魑");
                    Thread.sleep(500);
                    System.out.print("魅");
                    Thread.sleep(500);
                    System.out.print("魍");
                    Thread.sleep(500);
                    System.out.print("魉");
                    System.out.println( );

                } catch (Exception e) {
                    e.printStackTrace();

            }

与synchronized实例方法的区别:

  1. 锁的对象不同:

    • synchronized实例方法锁的是当前对象实例(this)。
    • synchronized static方法锁的是类的字节码对象。
  2. 同步范围不同:

    • 对于实例方法,只有同一个对象实例的调用才会被同步。不同对象实例调用同一个实例方法是相互独立的,除非在方法内部有共享的状态。
    • 对于静态方法,无论有多少个对象实例,只要是对同一个类的静态方法的调用,都会被同步。

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

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

相关文章

LED显示屏原理及其系统组成

随着城市化进程的加快&#xff0c;LED显示屏的需求在各个行业中迅速增长。无论是用于广告宣传、信息发布&#xff0c;还是场馆显示&#xff0c;LED显示屏都扮演着重要的角色。然而&#xff0c;对于很多人来说&#xff0c;LED显示屏的工作原理及其系统组成可能并不为熟知。本文将…

589. N 叉树的前序遍历(递归法)

目录 一&#xff1a;题目&#xff1a; 二&#xff1a;代码&#xff1a; 三&#xff1a;结果&#xff1a; 一&#xff1a;题目&#xff1a; 给定一个 n 叉树的根节点 root &#xff0c;返回 其节点值的 前序遍历 。 n 叉树 在输入中按层序遍历进行序列化表示&#xff0c;每…

Java JNA调用C函数常见问题及解决方法

目录 1 undefined symbol&#xff1a;xxx2 Java映射C数组乱码3 Java使用String接收不到C函数返回的char*4 Unable to load DLL xxx.dll5 java.lang.UnsatisfiedLinkError: %1 不是有效的 Win32 应用程序6 无效的ELF头7 Structure array elements must use contiguous memory8 j…

备考计算机二级Python之Day4下篇

实例解析--猜数字游戏 编写一个“猜数字游戏”的程序&#xff0c;在1~1000之间随机产生一个数&#xff0c;然后请用户循环猜这个数字&#xff0c;对于每个答案只回答“猜大了”或“猜小了”&#xff0c;直到猜对为止。输出用户的猜测次数。 使用Python语言的随机标准库random…

开源游戏开发引擎LayaAir

LayaAir是一款由Layabox公司推出的次世代全平台3D引擎&#xff0c;它支持2D、3D、VR与AR产品的开发&#xff0c;并允许开发者一次开发后同时发布为Web、小游戏、Native APP等多种平台的产品。 LayaAir引擎提供强大的IDE集成环境&#xff0c;包含3D场景编辑器、材质编辑器、粒子…

【linux中高级命令】

杀进程 1、lsof列出所有打开的文件&#xff0c;‌包括网络连接&#xff0c;‌从而提供关于系统状态的宝贵信息‌ #可以直接查看端口的进程 lsof -i:端口获取到进程&#xff0c;使用kill命令和PID来终止进程 kill -9 [PID]2、列出使用该端口的进程信息&#xff0c;‌包括PID …

linux上datax 安装以及使用

前言 DataX 是一款由阿里巴巴开源的数据同步工具&#xff0c;旨在帮助用户实现不同数据源之间的高效数据迁移和同步。无论是从传统的关系型数据库、NoSQL 数据库&#xff0c;还是到大数据存储系统&#xff0c;DataX 都能够轻松应对各种数据同步需求。通过简单的配置和灵活的插…

【解压即玩】最终幻想7 重制版中文+预购特典+全DLC,难忘的一作

数年前&#xff0c;一家名为神罗的公司&#xff08;起初称为神罗工程所&#xff09;发现了一种深埋于地底的神秘生物遗骸&#xff08;被称为杰诺瓦&#xff09;以及一种名为“魔晄”的能源。这家公司通过将这种生物遗骸浸泡在魔晄中来生产电力&#xff0c;从而迅速崛起成为全球…

一套在线工具管理服务器+DB+Redis+Mongo等

Team IDE是一个基于Web的、集成了多种开发工具和服务&#xff08;MySql、Oracle、金仓、达梦、神通等数据库、SSH、FTP、Redis、Zookeeper、Kafka、Elasticsearch、Mongodb&#xff09;的一体化开发环境&#xff0c;它不仅为开发者提供了便捷的开发体验&#xff0c;还支持团队协…

【文本 >>> 语音】⭐️SpringBoot 结合 jacob 简单实现一个文本朗读功能

目录 &#x1f378;前言 &#x1f37b;一、环境准备 &#x1f37a;二、依赖引入 &#x1f49e;️三、简单启动 &#x1f379;四、接口改造 4.1 封装为一个工具类 4.2 暴露一个接口 4.3 测试 &#x1f331;五、扩展 &#x1f378;前言 小伙伴们大家好&#xff0c;上次…

初学Python如何快速入门(内附详细攻略),一文讲清

目前python可以说是一门非常火爆的编程语言&#xff0c;应用范围也非常的广泛&#xff0c;工资也挺高&#xff0c;未来发展也极好。 Python究竟应该怎么学呢&#xff0c;我自己最初也是从零基础开始学习Python的&#xff0c;给大家分享Python的学习思路和方法。一味的买书看书…

AI指挥细胞大变身,脑瘤生存率跃升75%?

“ 胶质母细胞瘤&#xff08;GBM&#xff09;&#xff0c;一种凶险的脑癌&#xff0c;曾让无数家庭陷入绝望。然而南加州大学凯克医学院的一项突破性研究&#xff0c;利用AI技术将脑癌细胞转化为免疫细胞&#xff0c;为GBM患者带来了前所未有的生存希望。 ” AI识别与重编程…

Windows系统注册表

一、修改文件图标 电脑程序怎么更换python文件的图标_mob64ca12d12b68的技术博客_51CTO博客 二、 1.打开注册的命令&#xff1a; 按winR快捷键&#xff0c;在弹出的窗口中输入&#xff1a;regedit 2.注册表样式 已学习至&#xff1a;0&#xff1a;47&#xff1a;32 课堂链…

OpenAI Sora:视频生成领域的创新力量

一、Sora 的诞生与技术原理 Sora 模型由 OpenAI 推出&#xff0c;其诞生背景与人工智能技术在多模态领域的不断探索和发展紧密相关。在视频数据日益丰富和重要的时代&#xff0c;对视频生成模型的需求愈发迫切&#xff0c;Sora 应运而生。 Sora 独特的技术原理使其在众多模型…

验证码短信怎么实现Java接口对接

在当今数字化时代&#xff0c;商家和企业对于用户身份验证的需求日益增强&#xff0c;这不仅是出于保护用户数据安稳的考虑&#xff0c;也是维护业务正常运营、防止欺诈行为的重要一环。在众多身份验证手段中&#xff0c;文字验证码短信因其便捷性和低成本特性&#xff0c;成为…

面向对象03:创建对象内存分析

本节内容视频链接&#xff1a;面向对象06&#xff1a;创建对象内存分析_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV12J41137hu?p65&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 创建对象时的内存分析涉及到理解对象如何在内存中分配和管理。‌这个过程在…

Python酷库之旅-第三方库Pandas(096)

目录 一、用法精讲 411、pandas.DataFrame.values属性 411-1、语法 411-2、参数 411-3、功能 411-4、返回值 411-5、说明 411-6、用法 411-6-1、数据准备 411-6-2、代码示例 411-6-3、结果输出 412、pandas.DataFrame.axes属性 412-1、语法 412-2、参数 412-3、…

2024年【山东省安全员B证】最新解析及山东省安全员B证找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年山东省安全员B证最新解析为正在备考山东省安全员B证操作证的学员准备的理论考试专题&#xff0c;每个月更新的山东省安全员B证找解析祝您顺利通过山东省安全员B证考试。 1、【多选题】《建设工程安全生产管理条…

dompdf导出pdf中文乱码显示问号?、换行问题、设置图片大小

环境&#xff1a;PHP 8.0 框架&#xff1a;ThinkPHP 8 软件包&#xff1a;phpoffice/phpword 、dompdf/dompdf 看了很多教程&#xff08;包括GitHub的issue、stackoverflow&#xff09;都没有解决、最终找到解决问题的根本&#xff01; 背景&#xff1a;用Word模板做转PDF…