Java-线程同步

2023-11-04

Java-线程同步

在Java中,我们通过同步机制,来解决线程的安全问题。

实现线程安全的三种方法:

1.同步代码块

synchronized(同步监视器){
    //需要被同步的代码
}

说明:

  1. 操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。

  2. 共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。

  3. 同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。要求:多个线程必须要共用同一把锁

    补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
    在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。

    代码举例:

    继承Thread类的多线程同步代码块的实现

package com.lmw.thred1;

/**
 * @author 
 * @version 1.0.0
 * @createTime 2022/5/14 11:03
 * @description 使用同步代码块来处理继承Thread类时的线程安全问题
 */

public class WindowTest1 {
    public static void main(String[] args) {
        // 创建子线程1的对象
        MyWindow m1 = new MyWindow();
        MyWindow m2 = new MyWindow();
        MyWindow m3 = new MyWindow();
        // 设置子线程1 的名称
        m1.setName("线程1");
        // 启动子线程1
        m1.start();

        m2.setName("线程2");
        m2.start();

        m3.setName("线程3");
        m3.start();
    }
}

class MyWindow extends Thread {
    // 通过 声明静态变量的方式实现不同对象控制对一个变量
    private static int ticket = 3;

    @Override
    public void run() {

        while (true){
            // 使用synchronized 锁住同一个对象,这个对象对应调用它的对象要唯一,**多个线程必须要共用同一把锁
            synchronized (MyWindow.class){
                // 操作共享数据的代码块,被锁包住
                if(ticket > 0) {
                    System.out.println(getName() + "卖票,票号为:" + ticket);
                    ticket --;
                }else {
                    break;
                }

            }
        }
    }

}

实现Runnable接口的多线程同步代码块的实现

package com.lmw.thred1;

/**
 * @author 
 * @version 1.0.0
 * @createTime 2022/5/14 11:27
 * @description 使用同步代码块解决实现Runnable接口的线程安全问题
 */

public class Window2 {
    public static void main(String[] args) {
        MyWindow2 m2 = new MyWindow2();
        // 创建多个线程时不需要再创建多个对象
        Thread t1 = new Thread(m2);
        t1.setName("线程1");
        t1.start();

        Thread t2 = new Thread(m2);
        t2.setName("线程2");
        t2.start();

        Thread t3 = new Thread(m2);
        t3.setName("线程3");
        t3.start();

    }
}

class MyWindow2 implements Runnable {
    private int ticket = 3;

    @Override
    public void run() {

        while (true) {
            // 此时this 是唯一的 MyWindow2 对象
            synchronized (this) {
                if(ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
                    ticket --;
                }else {
                    break;
                }

            }

        }

    }
}

2.同步方法

如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的

在方法体声明时加上 synchronized ,特别地继承Thread类是需要将操作共享数据的方法声明为静态的

说明:

  1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
  2. 非静态的同步方法,同步监视器是:this
  3. 静态的同步方法,同步监视器是:当前类本身 类.class

​ 代码举例:

继承Thread类的多线程同步方法的实现

package com.lmw.thred1;

/**
 * @author limingwei6
 * @version 1.0.0
 * @createTime 2022/5/14 14:04
 * @description 使用同步方法来处理继承Thread类时的线程安全问题
 */

public class WindowTest3 {
    public static void main(String[] args) {
        MyWindow3 m1 = new MyWindow3();
        m1.setName("线程1");
        m1.start();

        MyWindow3 m2 = new MyWindow3();
        m2.setName("线程1");
        m2.start();

        MyWindow3 m3 = new MyWindow3();
        m3.setName("线程1");
        m3.start();

    }
}

class MyWindow3 extends Thread {
    private static int ticket = 3;
    boolean flag = true;
    @Override
    public void run() {
        while (flag) {
            flag = show();
        }
    }

    private static synchronized boolean show() {// 同步监视器 MyWindow3.class
//    private synchronized boolean show() {// 同步监视器有三个: t1,t2,t3,这样不能解决线程同步问题
        if(ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
            ticket --;
            return true;
        }else {
            return false;
        }
    }
}

实现Runnable接口多线程方法的实现

package com.lmw.thred1;

/**
 * @author 
 * @version 1.0.0
 * @createTime 2022/5/14 14:05
 * @description 使用同步方法解决实现Runnable接口的线程安全问题
 */

public class WindowTest4 {
    public static void main(String[] args) {
        MyWindow4 m1 = new MyWindow4();

        Thread t1 = new Thread(m1);
        t1.setName("线程1");
        t1.start();

        Thread t2 = new Thread(m1);
        t2.setName("线程2");
        t2.start();

        Thread t3 = new Thread(m1);
        t3.setName("线程3");
        t3.start();

    }
}

class MyWindow4 implements Runnable {
    private int ticket = 3;
    @Override
    public void run() {

        while (true) {
            if (ticket > 0) {
                show();
            }else {
                break;
            }
        }

    }
    // 操作共享数据的方法在一个方法中, 在声明方法时加 synchronized 给这个方法加锁
    private synchronized void show() {// 同步监视器是this
        if(ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
            ticket --;
        }
    }

}

3.Lock锁

package com.lmw.thred1;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author
 * @version 1.0.0
 * @createTime 2022/5/14 14:41
 * @description 使用Lock接口的方式解决多线程的线程同步安全
 */

public class LockTest {
    public static void main(String[] args) {
        MyWindow5 m2 = new MyWindow5();
        // 创建多个线程时不需要再创建多个对象
        Thread t1 = new Thread(m2);
        t1.setName("线程1");
        t1.start();

        Thread t2 = new Thread(m2);
        t2.setName("线程2");
        t2.start();

        Thread t3 = new Thread(m2);
        t3.setName("线程3");
        t3.start();
    }
}


class MyWindow5 implements Runnable {
    private int ticket = 3;
    //1. 声明一个 实现Lock接口的类 ReentrantLock 的对象 ,可以接收参数,true表示平均分配
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                //2.调用lock.lock 加锁方法
                lock.lock();
                if(ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
                    ticket --;
                }else {
                    break;
                }
            }finally {
                //3.解锁
                lock.unlock();
            }

        }
    }
}

4.线程同步的利弊:

  1. 同步的方式,解决了线程的安全问题。
  2. 操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。

5.三种方式对比

synchronized 与 Lock的异同?

  • 相同:二者都可以解决线程安全问题
  • 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
  •    Lock需要手动的启动同步(lock(),同时结束同步也需要手动的实现(unlock())
    

使用的优先顺序:

  • Lock —> 同步代码块(已经进入了方法体,分配了相应资源 ) —> 同步方法(在方法体之外)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java-线程同步 的相关文章

  • 从txt文件中读取数据而不下载它?

    我想从提供的文本文件中解析信息 有没有一种方法可以在应用程序中执行此操作 而无需先下载文件 以某种方式传输文本内容 打开到 URL 的 Http 连接 使用内置 HttpURLConnection 或使用 commons httpclien
  • 使用itext java库复制时pdf文件大小大大增加

    我正在尝试使用 Java 中的 itextpdf 库将现有的 pdf 文件复制到一些新文件中 我使用的是 itextpdf 5 5 10 版本 我在两种方式上都面临着不同的问题 PDFStamper 和 PdfCopy 当我使用 PDFSt
  • 如何重复一段文本中的每个字母?爪哇语

    就像在口吃中一样 如果文本为 dean 并且乘数为 3 则结果将是 dddeeeaaannn 由提供的乘数指定的次数 public static void repeatLetters String text dean int n 3 Str
  • java中高效的输入流到字符串方法

    因此 我在 Java 中的 诚然非常简单 应用程序上运行探查器 令我惊讶的是 仅次于需要在时间上发出 HTTP 请求的方法的是我的方法 inputStreamToString方法 目前它的定义如下 public static String
  • 无法在 Intellij 中运行主类[重复]

    这个问题在这里已经有答案了 我有以下项目结构 ProjectRoot src Main examples libs My src文件夹被标记为sources在 Intellij 中 现在 当我想运行 Main 类时 出现以下错误 Excep
  • Jenkins 未显示 Maven 编译器错误

    在 Jenkins 中构建多模块 maven 3 项目时 如果出现构建错误 我们会收到一条神秘消息 表明 Maven 编译器插件失败 这在上周才刚刚开始发生 INFO BUILD FAILURE INFO INFO Total time 1
  • Android 信号 11 (SIGSEGV),代码 1 (SEGV_MAPERR) libwebviewchromium.so

    对于 android 4 4 我多次收到 Native crash at system lib libwebviewchromium so 错误 以下是设备包括 Xperia Z1 SO 01F 16 30 2 Galaxy Tab4 7
  • 传递自定义类型查询参数

    如何接受自定义类型查询参数 public String detail QueryParam request final MYRequest request 上面的行在启动服务器时出现错误 jersey server model ModelV
  • 我的 Kafka 流应用程序刚刚退出,代码为 0,什么也不做

    为了尝试 Kafka 流 我这样做了 public static void main String args final StreamsBuilder builder new StreamsBuilder final Properties
  • 扩展多个类

    我知道 Java 不支持多重继承 因为不允许扩展多个类 我只是想知道我的问题是否有解决方法 我有一个名为CustomAction需要扩展两个抽象类 BaseAction and QuoteBaseAction 我无法更改这些抽象类中的任何一
  • javadoc 子集/java 库组织

    我自己从来没有运行过javadoc 无论是在命令行还是ant 的 javadoc 任务 http ant apache org manual Tasks javadoc html 我将使用 ant 我需要为我编写的库生成 javadoc 问
  • 在 Eclipse RCP 应用程序中禁用插件贡献

    我经常遇到这个问题 但尚未找到解决方案 每当我编写一个新的基于 Eclipse RCP 的应用程序并包含来自 Eclipse 平台的插件时 我都会 继承 其中一些插件的 UI 贡献 大多数贡献 菜单项 键盘快捷键 属性页 都很有用 但有时我
  • 如何在 JASPIC 中保存经过身份验证的用户?

    我开发了一个安全认证模块 SAM 并实现了validateRequest方法 我还有一个简单的 Web 应用程序配置为使用此 SAM In my validateRequest方法 我检查 clientSubject 并设置一个Caller
  • 难以理解 通配符

    我有一个非常基本的问题 下面的代码无法编译 假设 Apple Extends Fruit List
  • Spring 如何在运行时获取有关“强类型集合”的泛型类型信息?

    我在 Spring 3 0 文档中阅读了以下内容 强类型集合 仅限 Java 5 在 Java 5 及更高版本中 您可以使用强类型集合 使用泛型类型 也就是说 可以声明一个 Collection 类型 使其只能包含 String 元素 例如
  • 为什么/何时应该使用泛型方法?

    学习Java的时候遇到过通用方法 public
  • 使用 Hibernate Envers 的复合表

    我有一个带有复合表的应用程序 其中包含一个额外的列 一切正常 直到我们添加 Hibernate Envers Audited org hibernate MappingException 无法读取 no pack response Resp
  • 假布尔值=真?

    我在一本书中找到了这段代码 并在 Netbeans 中执行了它 boolean b false if b true System out println true else System out println false 我只是不明白为什
  • Axis2 的 wsdl2java 在 RPC/Encoded 样式 Web 服务上失败

    Axis2 有替代方案吗 或者让它工作的方式 例如不同的数据绑定 Retrieving document at Exception in thread main org apache axis2 wsdl codegen CodeGener
  • JPA ManyToMany 产生的空联接表

    我有一个应用程序 其中我尝试使用 Hibernate 作为 JPA 提供程序来实现两个实体之间的多对多关系 我正在尝试的例子是一个单向的 其中一个相机可以有多个镜头 而镜头可以安装到多个相机中 以下是我的实体类 只需粘贴其中的相关部分 Ca

随机推荐

  • 二分查找算法c语言函数,BinarySearch 经典二分查找算法

    前言 二分查找算是最经典也最入门的算法了 大一新生刚学C语言就开始写 但是看似简单的二分算法 想要考虑周全写得完美也是要费点功夫 比如java库里的二分查找 一个溢出的bug还存在了很久 更有号称90 程序员写不出无BUG的二分查找程序 夸
  • 亲测有效!帮你更方便更舒服使用ubuntu20.04!!!

    今天要记录的是如何更舒服的使用ubuntu20 04 全部内容就在上面这张图里 包括三方面 1 ubuntu美化 2 ubuntu扩展 3 必备软件 1 ubuntu美化 这部分内容可以直接参考 这位大佬 讲的很详细也很清楚 需要做一点补充
  • 【JVM 内存结构

    内存结构 前言 简介 程序计数器 定义 作用 特点 示例 应用场景 主页传送门 传送 前言 Java 虚拟机的内存空间由 堆 栈 方法区 程序计数器和本地方法栈五部分组成 简介 JVM Java Virtual Machine 内存结构包括
  • 异常相关面试题

    1 java中的异常继承体系及常见运行时异常 Throwable类是所有异常或错误的超类 它有两个子类 Error和Exception 分别表示错误和异常 其中异常Exception 分为运行时异常 RuntimeException 和编译
  • js合并数组对象(将数组中具有相同属性对象合并到一起,组成一个新的数组)

    一 根据数组对象中某一key值 合并相同key值的字段 到同一个数组对象中 组成新的数组 1 原数组 var array id 1 name Alice id 2 name Bob id 1 age 25 id 3 name Charlie
  • 机器学习 | Sklearn中的朴素贝叶斯全解

    前期文章介绍了朴素贝叶斯理论 掌握理论后如何去使用它 是数据挖掘工作者需要掌握的实操技能 下面来看看Sklearn中都有哪些朴素贝叶斯 朴素贝叶斯是运用训练数据学习联合概率分布 及
  • uniapp微信小程序实现对H5的全屏适配(@莫成尘)

    复制代码您将看到和一下截图一样的效果 我们将适配全屏至正常h5下的所以页面大小 您再此处将依然使用rpx作为开发单位
  • Linux网络编程 - 基于TCP的服务器端/客户端(1)

    一 理解 TCP 和 UDP 根据数据传输方式的不同 基于网络传输协议的套接字一般分为TCP套接字和UDP套接字 因为TCP是面向连接的 因此又称为基于流 stream 的套接字 TCP Transmission Control Proto
  • Android recyclerView只显示一条数据

    recyclerView的数据集合里明明很多条数据 为什么只显示了一条数据 代码里一顿debug过后 还去翻onBindViewHolder的注释文档 你是不是还是死活找不到原因 骚年 该扇自己耳光了 把item的高度设置成了match p
  • linux环境变量和软件安装路径 小结

    linux环境变量和软件安装路径 小结 目录 linux环境变量和软件安装路径 小结 1 背景 2 Linux环境变量设置 1 对所有用户永久生效 2 对单一用户永久生效 3 当前shell BASH 临时有效 4 查看环境变量 expor
  • sort函数自定义排序

    sort函数自定义排序 咳咳 自定义排序说实话用的地方还是很多的 像ACM里面 天梯赛里面 特别是天梯 必出这玩意 咳咳 水点字数 咱继续 首先哈 我们来看一下c 自带的排序 可以看出哈 默认是按升序排序的 sort不但可以对整型进行排序还
  • VTK交互器

    VTK交互器定义了用户了与VtkWidget界面的交互方式 结构图如下 交互器类名 功能 vtkInteractorStyle 一个实现大部分动作的基类 对交互只有接口 没有实际功能 vtkInteractorStyle3D 允许用户交互
  • 阿里云ECS服务器Linux第一次登录 提示Login Incorrect的解决方法

    问题情景 1 第一次购买ECS服务器 2 没有设置过系统root账户的Login密码 不是连接远程时提示需要输入的那个6位纯数字密码哈 3 在阿里云控制台中启动 远程连接 4 输入6位纯数字密码 首次连接会提示密码 后面不再提示 需要记下来
  • 小白就懂的IDEA中将本地的Jar包导入到Springboot(含若依)中

    在项目开发的过程中 难免会遇到在Springboot中无法通过pom中添加依赖使用maven下载需要的jar包 而本地PC端中我们拥有jar包 这时候该如何办呢 下面所采用的方法既不添加lib文件 就能搞定 1 打开IDEA开发软件 2 然
  • 『贪吃蛇』AI 算法简易实现(中秋特别版)

    前言 一年一度的中秋节就快到了 平台也有各种各样的中秋发文活动 正在翻阅时偶然间我看到了这篇文章 兔饼大作战 吃月饼 见月亮 还能咬自己 欢庆中秋特制版 掘金 juejin cn 大家肯定比较熟悉了 这个游戏的内核就是贪吃蛇 作者也是对玩法
  • G1理论基础与最佳实践

    文章目录 1 G1理论基础 1 1 G1介绍 1 2 YongGC 1 3 mixed gc 1 4 扩展 1 4 1 记忆集和卡表 1 4 2 STAB和TAMS 1 5 G1相比CMS的优势 2 G1日志解读与经验分享 2 1 日志解读
  • Win10专业版系统Docker安装、配置和使用详细教程

    一 win10专业版系统首先需要开启硬件虚拟化及Hyper V功能 才能进行Docker for Windows软件安装 如何开启硬件虚拟化 自行百度 可在任务栏中查看虚拟化是否开启 win10系统 打开控制面板 应用 程序和功能 开启Hy
  • postman——集合——执行集合——迭代运行集合

    网址 https learning getpostman com docs postman collection runs starting a collection run 开始收集运行 集合是一组请求 可以在对应的环境下作为一系列请求一
  • 前后端接口规范

    原文地址 https github com f2e journey treasure blob master api md 前后端接口规范 随着前后端分离越来越普遍 后端接口规范也就越来越重要了 一套良好的接口规范可以提升工作效率 减少沟通
  • Java-线程同步

    Java 线程同步 在Java中 我们通过同步机制 来解决线程的安全问题 实现线程安全的三种方法 1 同步代码块 synchronized 同步监视器 需要被同步的代码 说明 操作共享数据的代码 即为需要被同步的代码 gt 不能包含代码多了