首页 > 编程语言 >【Java】同步方法

【Java】同步方法

时间:2024-07-19 23:55:55浏览次数:19  
标签:同步 Java Thread StringBuffer 代码 ticket 方法

文章目录

一、总述

刚刚我们已经学习完了同步代码块,就是将一段代码锁起来,这样就可以解决多线程操作共享数据时带来的数据安全问题。

但是如果我们想要将一个方法里面所有的代码全部锁起来,此时就没有必要去用同步代码块了,我们可以直接将 synchronized 加在方法上,此时这个方法就叫做同步方法。

修饰符 synchronized 返回值类型 方法名(方法参数) { 
	方法体;
}

特点1:同步方法是锁住方法里面所有的代码

特点2:同步方法的锁对象,我们是不能自己指定的,是Java已经规定好的。

  • 如果你当前的方法是非静态的,它的锁对象就是 this,即当前方法的调用者。
  • 如果你当前的方法是静态的,它的锁对象就是 当前类的字节码文件对象

二、练习

题目还是刚刚的题目,只不过要求用同步方法完成。

很多同学在写同步方法的时候都会有一个小疑问:不知道把哪些方法写在同步方法中。

技巧:你首先不要写同步方法,而是先写同步代码块,然后再把同步代码块里面的代码去抽取成方法就行了。

需求:
     某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
     利用同步方法完成
     技巧:同步代码块

我们先用同步代码块写,再去改成同步方法。

由于在之前我们用的都是多线程的第一种实现方式,这里就使用第二种实现方式。

由于现在使用的是第二种实现方式,此时 ticket 的前面就没必要去加 static 了。

我们之前一直使用第一种实现方式(继承Thread类),此时它是有可能创建多个对象的,所以如果我想让所有的对象都共享一个 ticket成员变量的值,在前面就需要加static。

image-20240506182109806

但是我们现在使用的是第二种实现方式(实现Runnable接口),在这种方式下,MyRunnable(我们自己写的类)只有可能会创建一次,它是作为一个参数让线程去执行的,所以只会创建一次它的对象。

那么既然只会创建一次,ticket就没有必要再去加static了。

public class MyRunnable implements Runnable {
    int ticket = 0;
    @Override
    public void run() {
    }
}

接下来完善 run(),写 run() 的时候也是有套路的

public class MyRunnable implements Runnable {

    int ticket = 0;

    @Override
    public void run() {
        //1.循环
        while (true) {
            //2.同步代码块(同步方法)
            //3.判断共享数据是否到了末尾,如果到了末尾
            if (ticket == 100) {
                break;
            } else {
                //4.判断共享数据是否到了末尾,如果没有到末尾
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ticket++;
                System.out.println(Thread.currentThread().getName() + "在卖第" + ticket + "张票!!!");
            }
        }
    }
}

测试类

MyRunnable mr = new MyRunnable();

Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);

t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");

t1.start();
t2.start();
t3.start();

接下来改成同步方法。

改写很简单:将synchronized里面所有的代码全都放到方法中即可。

选中synchronized里面所有的代码,ctrl + alt + M ,此时就可以抽取成一个方法了。

public class MyRunnable implements Runnable {

    int ticket = 0;

    @Override
    public void run() {
        //1.循环
        while (true) {
            //2.同步代码块(同步方法)
            if (method()) break;
        }
    }

    //this
    private synchronized boolean method() {
        //3.判断共享数据是否到了末尾,如果到了末尾
        if (ticket == 100) {
            return true;
        } else {
            //4.判断共享数据是否到了末尾,如果没有到末尾
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticket++;
            System.out.println(Thread.currentThread().getName() + "在卖第" + ticket + "张票!!!");
        }
        return false;
    }
}

三、StringBuffer

学习完同步方法后,我们之前有很多知识点其实也就可以解释了。

很多同学在之前肯定听过:字符串在进行拼接的时候,除了 StringBuilder 外,还有一个类:StringBuffer

那这个 StringBuffer 跟我们之前学习的 StringBuilder 有什么区别呢?

打开 API帮助文档 来看一下。

这里打开了两个 API帮助文档,左边是 StringBuilder,右边是 StringBuffer

image-20240506192619700

往下滑,可以惊讶的发现,方法都是一模一样的。

image-20240506192721093

那为什么Java要定义两个完全一模一样的类呢?

看下面,StringBuilder下面有这样一句话:将 StringBuilder 的实例(即对象的意思)用于多个线程是不安全的。如果需要这样的同步,则建议使用 StringBuffer

image-20240506192904651

接下来看下源码,同样左边是 StringBuilder,右边是 StringBuffer

往下拉,找到成员方法。可以发现,StringBuffer 里面每一个成员方法都有 synchronized,但左边的 StringBuilder 所有的成员方法都没有 synchronized,这就表示 StringBuffer 是线程安全的,因为它里面所有的方法前面都有 synchronized,即它里面所有的方法都是同步的。

image-20240506194200650

那我们以后如何选择呢?

如果你的代码是单线程的,不需要考虑多线程中数据安全的情况,就用 StringBuilder 就行了。

但如果是多线程环境下,需要考虑到 数据安全,这时候就可以选择右边的 StringBuffer

标签:同步,Java,Thread,StringBuffer,代码,ticket,方法
From: https://blog.csdn.net/qq_39921135/article/details/140428637

相关文章

  • java多线程
    程序,进程,线程,并行,并发程序是静态的,进程process是动态的一个进程至少有一个线程多线程程序优点提高应用程序的响应提高CPU利用率改善程序结构并行parallel,指两个或多个事件在同一时刻发生并发concurrency,两个或多个事件在同一个时间段内发生,宏观上是多个进程同步进行......
  • 在 Java 中,怎样设计一个可扩展且易于维护的微服务架构?
    在Java中设计一个可扩展且易于维护的微服务架构,可以考虑以下几个方面:模块化设计:将应用拆分为多个小的、独立的模块,每个模块负责处理特定的业务逻辑。每个模块可以独立开发、测试和部署,增加或替换模块时不会影响其他模块。使用轻量级的通信机制:微服务之间通过RESTfulAPI......
  • JAVA面试框架篇(SSM和MyBatis)
    框架篇一.Spring1.Spring1.1Bean生命周期1.2Bean循环依赖(引用)说说spring中的循环引用构造方法出现了循环依赖怎么解决?1.3Bean线程安全问题问题:Spring中的Bean是线程安全的吗?1.4AOP(什么是AOP?)AOP:AspectOrientedProgramming面向切面编程应用场景(你们项目中有没有......
  • 初级java每日一道面试题-2024年7月19日
    在Java中,重载(Overloading)和重写(Overriding)是面向对象编程中多态性的两个重要概念。1.重载(Overloading)定义:重载是指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。也就是说,这些方法的名称相同,但参数的个数、类型或顺序至少有一个不同。目的:重载......
  • javaScript常用对象
    1. Array对象JavaScriptArray对象用于定义数组1.1定义格式数组的定义格式有两种:方式1var变量名=newArray(元素列表);例如:vararr=newArray(1,2,3);//1,2,3是存储在数组中的数据(元素)方式2var变量名=[元素列表];例如:vararr=[1,2,3];//1,2,3是......
  • JavaWeb学习笔记后端部分
    后端Web开发MavenApacheMaven是一个项目管理和构建工具,它基于项目对象模型(POM)的概念,通过一小段描述信息来管理项目的构建。Maven作用依赖管理统一的项目结构项目构建仓库安装Maven<mirror><id>alimaven</id><name>aliyunmaven</name><url>h......
  • Java入门基础:Java中的标识符;Java常量与变量;Java基本数据类型;Java运算符
    一,Java标识符【1】标识符:读音 biaozhifu (注意是标zhi符,不是标shi符,“识”这个字是多音字,我也是才发现^_^,你呢?)【2】标识符是?     包,类,变量,方法.....等等,只要是起名字的地方,那个名字就是标识符。【3】标识符定义规范:  1.四个可以(组成部分):数字,字母,下划......
  • 7. 基本数据类型的内置方法
    1.列表的内置方法1.1优先掌握的内置方法1.1.1强制类型转换可以将可迭代类型转换为列表类型print(list('messi'))#['m','e','s','s','i']print(list((1,2,3)))#[1,2,3]print(list({4,5,6}))#[4,5,6]list方法在强制转换字典的时候默......
  • Java基础-详解String类
    为什么String类是不可变的publicfinalclassString{privatefinalchar[]value;}由以上String类的源码可以看出,String类内部使用字符数组char[]来存储字符串(Java9后修改为byte[]字节数组),而且数组被final修饰且为私有的,String类没有提供修改该字符串方法,以及由于S......
  • Javaweb项目|电子商城购物平台的设计与开发+ssm小程序
    Javaweb项目|电子商城购物平台的设计与开发+ssm小程序收藏点赞不迷路 关注作者有好处文末获取源码一、系统展示二、万字文档展示 基于电子商城购物平台的设计与开发+ssm小程序开发语言:Java数据库:MySQL技术:Spring+SpringMVC+MyBatis+Vue工具:IDEA/Ecilpse、Navicat......