【多线程】-- 10线程同步synchronized方法/块

多线程

6 线程同步

同步方法

  • 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括以下两种用法:

synchronized方法和synchronized

  1. 同步方法:
public synchronized void nethod(int args) {}
  • synchronized方法控制对”对象“的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞。方法一旦执行,就会独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。

缺陷:若将一个大的方法声明为synchronized将会影响效率。在方法中,需要修改的内容才需要锁(只读的内容不必同步修改),锁的太多自然会浪费资源。

  1. 同步块:
synchronized(Obj) {}
  • Obj称为同步监视器
    • Obj可以是任何对象,但推荐使用共享资源作为同步监视器
    • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,即这个对象本身,或者是class(反射中会涉及)
  • 同步监视器的执行过程
    • 第一个线程访问,锁定同步监视器,执行其中代码
    • 第二个线程访问,发现同步监视器被锁定,无法访问
    • 第一个线程访问完毕,解锁同步监视器
    • 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

在上节所提到的三个不安全案例中,通过同步方法进行修改完善,可以发现代码运行结果发生了显著变化,并且是我们所期望看到的。

  • 买票案例改:
package com.duo.synchronize;

//不安全案例1:买票
public class UnsafeCase1 {

    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station, "Hua").start();
        new Thread(station, "Ming").start();
        new Thread(station, "Hong").start();
    }
}

class BuyTicket implements Runnable {

    private int ticketNum = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            buy();
        }
    }

    //添加修饰词synchronized之后,此方法变为同步方法,锁的是this
    private synchronized void buy() {
        if (ticketNum <= 0) {
            flag = false;
            return;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "抢到了第" + ticketNum-- + "张票");
    }
}

运行结果:

图1

在运行中,输出结果是间隔一秒挨个输出,且不再存在抢到同一张票的情况(即多个线程同时操作一个同一个对象),也没有负数出现。

  • 银行取钱案例改:
package com.duo.synchronize;

//不安全案例2:银行两人同时取钱
public class UnsafeCase2 {

    public static void main(String[] args) {
        Account account = new Account(1_000_000, "基金");

        Withdraw husband = new Withdraw(account, 500_000, "丈夫");
        Withdraw wife = new Withdraw(account, 1_000_000, "妻子");

        husband.start();
        wife.start();
    }
}

class Account {
    int balance;
    String name;

    public Account(int balance, String name) {
        this.balance = balance;
        this.name = name;
    }
}

class Withdraw extends Thread {

    Account account;  //账户
    int transaction;  //交易金额
    int change;  //现在手中的零钱总金额

    public Withdraw(Account account, int transaction, String name) {
        super(name);
        this.account = account;
        this.transaction = transaction;
    }

    //取钱
    @Override
    public void run() {  //若在此处添加synchronized修饰词并不会达到期望的效果,默认锁的是this

        synchronized (account) {
            if (account.balance - transaction < 0) {
                System.out.println(Thread.currentThread().getName() + ":账户余额不足,交易失败");
                return;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            account.balance -= transaction;
            change += transaction;

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //Thread.currentThread().getName() == this.getName()
            System.out.println(this.getName() + "手中当前金额:" + change);
            System.out.println(account.name + "账户当前余额为:" + account.balance);
        }

    }
}

运行结果:

图2

可以看到,通过synchronized可以控制对对象的访问。需要注意的是,此案例中不能直接在取钱的run()方法处添加synchronized修饰词,因为synchronized默认锁的是this,无法实现对账户account的锁定,故需将整个方法放入synchronized块中。

  • 线程的不安全集合改:
package com.duo.synchronize;

import java.util.ArrayList;
import java.util.List;

//不安全案例3:线程不安全的集合
public class UnsafeCase3 {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 5000; i++) {
            new Thread(() -> {
                synchronized (list) {
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(list.size());
    }
}

运行结果:

图3

可以看到,在将list.add(Thread.currentThread().getName());放入synchronized块中后,集合列表输出的元素个数大小等于设定值5000.

【补充】JUC安全类型的集合测试

package com.duo.synchronize;

import java.util.concurrent.CopyOnWriteArrayList;

//测试JUC安全类型的集合
public class JUCTest {

    public static void main(String[] args) {

        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();  //集合都加上泛型,规范

        for (int i = 0; i < 5000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(list.size());
    }
}

运行结果:

图4

可以发现,在使用JUC安全类型的集合时,即使没有使用synchronized方法或synchronized块包裹,最终输出结果也是安全的。


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

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

相关文章

初始化 Clouder Manager 数据库报错Unable to find JDBC driver for database type: MySQL

CDH部署初始化 Clouder Manager 数据库报错。 解决方法&#xff1a; 把mysql驱动放到 /usr/share/java/目录下面(没有的新建&#xff09;&#xff0c;驱动名称也要修改为mysql-connector-java.jar 再次执行后成功&#xff1a; sudo /opt/cloudera/cm/schema/scm_prepare_data…

javaweb mybatis(手动jar包)

基础&#xff1a;https://blog.csdn.net/qq_67832732/article/details/134764134 条件查询 在映射文件的SQL配置中配置参数 使用parameterType来指定参数类型 使用#{参数名}来接收参数的值 parameterType"string" 表示sql语句需要一个参数&#xff0c;类型为字符…

串行口的工作原理及应用

前言 对最近串行口的学习进行一下总结。 参考链接 【51单片机】串口通信 - 知乎 (zhihu.com) LED数码管的静态显示与动态显示&#xff08;KeilProteus&#xff09;_proteus数码管显示-CSDN博客 定时器/计数器的应用-CSDN博客 74ls164_百度百科 (baidu.com) 74ls165中文资…

Kafka 的特点和优势

Apache Kafka 作为一款分布式流处理平台&#xff0c;以其独特的特点和卓越的优势成为实时数据处理领域的瑰宝。本文将深入研究 Kafka 的各项特点和优势&#xff0c;并通过详实的示例代码展示其在不同场景下的强大应用。 高吞吐量和水平扩展 Kafka 的设计注重高吞吐量和水平扩…

使用外部编辑器编辑执行MAXScript代码的方法

如何使用外部编辑器编辑执行MAXScript代码&#xff1f;这里我们要借助一个3dMax插件程序MXSCOM&#xff0c;MXSCOM允许从外部代码编辑器编辑和执行3ds Max MaxScript和Python文件。 2005年&#xff0c;Simon Feltman发布了第一个MXSCOM&#xff0c;这是一个小型的Visual Basic …

【Delphi】实现彩色日志显示框

目录 一、前言 二、实现方法 1. 第一步 2. 第二步 3. 第三步 三、主程序代码 四、下载 1. 可执行程序 2. 程序源代码 一、前言 在用Delphi做日常开发的时候&#xff0c;经常需要显示程序运行的日志&#xff0c;一般我们会使用TMemo&#xff0c;使用起来简单&#xff0c…

根据关键词写作文章的软件,根据标题写作文章的工具

在当今信息化时代&#xff0c;人工智能技术的飞速发展&#xff0c;智能AI写作工具逐渐成为文案创作者的得力助手。这些工具不仅能够根据标题迅速生成文章&#xff0c;而且在提高创作效率的同时&#xff0c;也为我们节省了大量时间和精力。 人工智能的基本原理&#xff1a;人工智…

Python遥感开发之快速判断TIF数据为空

Python遥感开发之快速判断TIF数据为空 前言&#xff1a;介绍一下如何使用python下的gdal读取tif数据的时候&#xff0c;快速判断该tif数据是否为空&#xff0c;如果为空的话就把当前的tif删掉。 如图所示&#xff0c;通过arcgis查看箭头指向的为空值。 仅通过文件的大小无法判…

java 工具类: CompareUtils(比较对象字段值变化)

一、前言 我们在工作中&#xff0c;可能会在日志中记录数据的变化情况或者在公共处理的数据增加一个日志页面&#xff0c;记录每次修改的变化。我们可以根据CompareUtils工具类比较数据前后发生了怎样的变化, 这样我们就可以知道数据做了哪些改变. 二、条件限制 在写这个通用…

【Leetcode题单】(01 数组篇)刷题关键点总结01【数组的遍历】

【Leetcode题单】&#xff08;01 数组篇&#xff09;刷题关键点总结01【数组的遍历】&#xff08;4题&#xff09; Easy数组的遍历485. 最大连续 1 的个数 Easy495. 提莫攻击 Easy414. 第三大的数 Easy628. 三个数的最大乘积 Easy 大家好&#xff0c;这里是新开的LeetCode刷题系…

【数组和函数实战: 斗地主游戏】

目录 1. 玩法说明 2. 分析和设计 3. 代码实现 4. 游戏演示1. 玩法说明 一副54张牌,3最小,两个王最大,其实是2,和上面一样从大到小排列 2. 分析和设计 2.1 分析和设计 常量和变量设计 一副牌有54张,有牌的数值和花色,可以分别用两个数组来存储,card为卡牌表示的数值,color为…

Git 标签管理

前言 标签 tag&#xff0c;就相当于对 某一次的 commit 做一个标识&#xff0c;起了一个别名&#xff0c;例如&#xff1a;在某个项目发布版本的时候&#xff0c;可针对最后一次 commit 起一个别名 v1.0 来标识这一次的commit。tag 的作用&#xff1a;commit id 相对于 tag 是很…

openwrt上开启syslog打印方法

最近在openwrt上调试蓝牙时&#xff0c;出现问题&#xff0c;设备上的蓝牙适配器已经正常工作了&#xff0c;执行pair命令后&#xff0c;openwrt和待连接的设备上都出现了配对码&#xff0c;两边都同意&#xff0c;但连接失败 尝试分析log&#xff0c;发现在如下代码处打印了错…

代码随想录算法训练营 ---第五十二天

第一题&#xff1a; 简介&#xff1a; 动态规划五部曲&#xff1a; 1.确定 dp数组下标的定义 dp[i] 到达 i 时 最长递增子序列的长度 2.确定递推公式 我们确定当前的最大长度需要遍历前面所有的最大长度&#xff0c;然后如果序列最后一个值小于nums[i]那就dp[j] 1&#xf…

Redis--13--缓存一致性问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 缓存一致性问题1、先更新缓存&#xff0c;再更新DB方案二&#xff1a;先更新DB&#xff0c;再更新缓存方案三&#xff1a;先删缓存&#xff0c;再写数据库推荐1&…

Elk-filebeat

前言 Elk&#xff1a;filebeat搜集日志工具和logstash相同 Filebeat是一个轻量级的日志收集工具&#xff0c;所使用的资源比logstash部署和启动时使用的资源更小 Filebeat可以运行在非Java环境&#xff0c;他可以代理logstash在非Java环境上收集日志 Filebeat无法实现数据的…

【选择题】校招笔试选择题第一辑

题目 以下程序的运行结果是&#xff08; &#xff09; #include <stdio.h> int main(void) {printf("%s , %5.3s\n", "computer", "computer");return 0; }A. computer , puter B. computer , com C. computer , computer D. computer…

zookeeper+kafka+ELK+filebeat集群

目录 一、zookeeper概述&#xff1a; 1、zookeeper工作机制&#xff1a; 2、zookeeper主要作用&#xff1a; 3、zookeeper特性&#xff1a; 4、zookeeper的应用场景&#xff1a; 5、领导者和追随者&#xff1a;zookeeper的选举机制 二、zookeeper安装部署&#xff1a; 三…

STM32-SPI 中断

SPI协议 1.1 SPI总线介绍 SPI接口是Motorola &#xff08;motorola | Smartphones, Accessories & Smart Home Devices&#xff09;首先提出的全双工三线/四线同步串行外围接口采用主从模式&#xff08;Master Slave&#xff09;架构。 时钟由Master控制&#xff0c;在时钟…

【Leetcode题单】(01 数组篇)刷题关键点总结02【统计数组中的元素】

【Leetcode题单】&#xff08;01 数组篇&#xff09;刷题关键点总结02【统计数组中的元素】&#xff08;6题&#xff09; 统计数组中的元素645. 错误的集合 Easy697. 数组的度 Easy448. 找到所有数组中消失的数字 Easy442. 数组中重复的数据 Medium41. 缺失的第一个正数 Hard27…