首页 > 编程语言 >Java中数据同步-synchronized关键字与Mointor的使用

Java中数据同步-synchronized关键字与Mointor的使用

时间:2022-09-07 13:34:03浏览次数:117  
标签:Java Thread synchronized Mointor void 线程 static public

场景

Java中Thread类的常用API以及使用示例:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/126596884

在上面的基础上,学习线程同步的相关概念。

数据不一致问题引入

模拟一个营业厅叫号机程序

package com.ruoyi.demo.threadsafe;

/**
 * 模拟营业大厅叫号程序,每次会不一样的发现:某个号码被略过、某个号码被多次显示、号码超过了最大值500
 */
public class TicketWindowRunable implements Runnable{

    private int index = 1;
    private final static int MAX = 500;

    @Override
    public void run() {
        while (index <= MAX)
        {
            System.out.println(Thread.currentThread()+"的号码:"+(index++));
        }
    }

    public static void main(String[] args) {
        final TicketWindowRunable task = new TicketWindowRunable();
        Thread windowThread1 = new Thread(task,"1号窗口");
        Thread windowThread2 = new Thread(task,"2号窗口");
        Thread windowThread3 = new Thread(task,"3号窗口");
        Thread windowThread4 = new Thread(task,"4号窗口");

        windowThread1.start();
        windowThread2.start();
        windowThread3.start();
        windowThread4.start();
    }
}

多次运行上面程序,每次都会不一样。

 

 

可以通过输出的行数明显发现不一样,主要有三个问题:

某个号码被略过、某个号码被多次显示、号码超过最大值

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。

实现

1、出现上述不一样的原因是线程的执行是由CPU时间片轮询调度的,CPU调度器在将执行权交给各个线程时,多个线程对index变量(共享变量/资源)

同时操作引起的。

要解决这个问题需要使用synchronized关键字,其提供了一种排他机制,也就是在同一时间只能有一个线程执行某些操作。

2、将上面的叫号程序修改

 

package com.ruoyi.demo.threadsafe;

/**
 * 模拟营业大厅叫号程序
 */
public class TicketWindowRunableWithSync implements Runnable{

    private int index = 1;
    private final static int MAX = 500;
    private final static Object MUTEX = new Object();
    @Override
    public void run() {
        synchronized (MUTEX){
            while (index <= MAX)
            {
                System.out.println(Thread.currentThread()+"的号码:"+(index++));
            }
        }
    }

    public static void main(String[] args) {
        final TicketWindowRunableWithSync task = new TicketWindowRunableWithSync();
        Thread windowThread1 = new Thread(task,"1号窗口");
        Thread windowThread2 = new Thread(task,"2号窗口");
        Thread windowThread3 = new Thread(task,"3号窗口");
        Thread windowThread4 = new Thread(task,"4号窗口");

        windowThread1.start();
        windowThread2.start();
        windowThread3.start();
        windowThread4.start();
    }
}

 

 

此时程序运行多次,不会出现数据不一致的问题。

3、线程堆栈分析

synchronized关键字提供了一种互斥机制,在同一时刻,只能有一个线程访问同步资源。下面是一个简单示例

package com.ruoyi.demo.threadsafe;

import java.util.concurrent.TimeUnit;

public class Mutex {
    private final static Object MUTEX = new Object();
    public void accessResource(){
        synchronized (MUTEX){
            try {
                //TimeUnit.MINUTES.sleep(10);
                TimeUnit.SECONDS.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        final Mutex mutex = new Mutex();
        for (int i = 0; i < 5 ; i++) {
            new Thread(mutex::accessResource).start();
        }
    }
}

上面定义了一个方法accessResource,使用同步代码块的方式对accessResource进行同步,同时定义5个线程调用accessResource方法。

由于同步代码块的互斥性,只能有一个线程获取了mutex monitor的锁,其他线程只能进入堵塞状态。

4、使用JConsole工具监控

找到jdk的目录下bin下jconsole.exe,双击运行

新建连接-本地进程-上面的Mutex选中-连接-接受不安全的连接-线程-Thread-0到4就是上面的5个线程

 

 

当设置休眠10分钟的时候可以看到,Thread-0在sleep

 

 

其它的则是进入了BLOCKED状态。

5、使用jps和jstack打印线程堆栈信息

通过上面的jconsole可以获取到其pid,也可以直接在cmd中输入

jps

获取所有的pid之后,再输入

jstack pid号

打印进程的线程堆栈信息

 

 

 

看到与上面的jconsole的效果一致。

6、规则与注意事项

规则:

每个对象都与一个monitor相关联,一个monitor的lock的锁只能被一个线程在同一时间获得。

释放对monitor的所有权的前提是你曾经获取到了所有权。

注意问题:

 ①与monitor关联的对象不能为空。

private final static Object MUTEX = null;

②synchronized作用域太大。

如果将其作用在run方法上,则丧失了并发的优势。

③不同的monitor企图锁相同的方法。

package com.ruoyi.demo.threadsafe;

public class ErrorMutexDemo implements Runnable {
    private final Object MUTEX = new Object();

    @Override
    public void run() {
        synchronized (MUTEX)
        {
           
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(ErrorMutexDemo::new).start();
        }
    }
}

上面的代码每个线程争抢的monitor关联引用都是彼此独立的,因此不能起到互斥的作用。

④多个锁的交叉导致死锁

7、This Monitor与Class Monitor介绍

ThisMonitor

两个方法method1和method2都被synchronized关键字修饰,启动两个线程分别访问method1和method2

package com.ruoyi.demo.threadsafe;

import java.util.concurrent.TimeUnit;

import static java.lang.Thread.currentThread;

public class ThisMonitor {
    public synchronized void method1(){
        System.out.println(currentThread().getName()+"enter to method1");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void method2(){
        System.out.println(currentThread().getName()+"enter to method2");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ThisMonitor thisMonitor = new ThisMonitor();
        //注意这里调用了构造方法public Thread(Runnable target, String name)
        //Target——线程启动时调用其run方法的对象。如果为空,则调用此线程的run方法。
        new Thread(thisMonitor::method1,"T1").start();
        new Thread(thisMonitor::method2,"T2").start();
    }
}

运行之后的结果为

 

 

此时用jstack分析

 

 

可以得出,synchronized关键字同步类的不同实例方法,争抢的是同一个monitor的lock,而与之相关联的引用则是ThisMonitor的实例

引用。

Classmonitor

有两个类方法(静态方法)分别使用synchronized对其进行同步

package com.ruoyi.demo.threadsafe;

import java.util.concurrent.TimeUnit;

import static java.lang.Thread.currentThread;

public class ClassMonitor {
    public static synchronized void method1()
    {
        System.out.println(currentThread().getName()+"enter to method1");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static synchronized void method2()
    {
        System.out.println(currentThread().getName()+"enter to method2");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Thread(ClassMonitor::method1,"T1").start();
        new Thread(ClassMonitor::method2,"T2").start();
    }
}

运行之后的效果

 

 

使用jstack分析之后

由此可知,用synchronized同步某个类的 不同静态方法争抢的也是同一个monitor的lock。

 以上代码和示例参考《Java高并发编程详解》,建议阅读原书。 

标签:Java,Thread,synchronized,Mointor,void,线程,static,public
From: https://www.cnblogs.com/badaoliumangqizhi/p/16665076.html

相关文章

  • Docker安装Java及环境配置
    一、安装jdk1、搜索javajdk#yumsearchjava|grepjdk2、选择安装版本#yuminstall-yjava-1.8.0-openjdk*3、安装完成后输入java-version查看安装的版本、ja......
  • Java8Stream流复习和api总结
    构建方式list.stream();Stream.of(list);基础常用APIStream<Number>stream=list.stream();//获取最大值stream.max(比较器);//获取最小值stream.min(比较器);......
  • 【面经】中兴java一面凉经
    中兴面经1、出示身份证2、自我介绍3、本科成绩排名、硕士成绩排名4、java的代码量5、抽象类和接口的差别,使用场景抽象类要被子类继承,接口要被类实现。接口只能做......
  • 力扣591(java)-标签验证器(困难)
    题目:给定一个表示代码片段的字符串,你需要实现一个验证器来解析这段代码,并返回它是否合法。合法的代码片段需要遵守以下的所有规则:代码必须被合法的闭合标签包围。否则,代......
  • 干货 | Elasticsearch Java 客户端演进历史和选型指南
    1、Elasticsearchjava客户端为什么要选型?Elasticsearch官方提供了很多版本的Java客户端,包含但不限于:Transport客户端JavaREST客户端LowLevelREST客户端Hi......
  • java 参数校验
    一controller参数校验参考:https://juejin.cn/post/68449039028112752781.当参数中有 org.springframework.validation.Errors的子类,例如 BindingResult,异常不会......
  • 在cmd中关闭所有java进程
    我们在Windows系统下使用Java软件,或者使用JavaIDE进行编程时,都会在运行这些Java软件时启动java.exe和javaw.exe。有时候因为一些预料之外的错误导致程序失去响应,这时候就......
  • ElasticSearch进阶:各种ES查询在Java中的实现
    注:本文摘自:https://mp.weixin.qq.com/s/7vEy-vN8JV3o6sAh6HFohA   本文基于elasticsearch7.13.2版本,es从7.0以后,发生了很大的更新。7.3以后,已经不推荐使用Transpo......
  • Java 的运算符
    Java语言支持如下运算符:算术运算符:+(加),-(减),*(乘),/(除),%(取余),++(自增),--(自减)操作符描述例子+加法-相加运算符两侧的值A+B等于30-减法-左操作数减去右操......
  • java poi - excel cell 设置自定义颜色
    XSSFCellStylecellStyle=wb.createCellStyle();cellStyle.setFillForegroundColor(newXSSFColor(newColor(195,227,255)));cellStyle.setFillPattern(FillPatter......