当前位置: 首页 > 编程笔记 >

Java并发 synchronized锁住的内容解析

夹谷烨赫
2023-03-14
本文向大家介绍Java并发 synchronized锁住的内容解析,包括了Java并发 synchronized锁住的内容解析的使用技巧和注意事项,需要的朋友参考一下

synchronized用在方法上锁住的是什么?

锁住的是当前对象的当前方法,会使得其他线程访问该对象的synchronized方法或者代码块阻塞,但并不会阻塞非synchronized方法。

脏读

一个常见的概念。在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过的。注意这里 局部变量是不存在脏读的情况

public class ThreadDomain13
{
  private int num = 0;
  
  public void addNum(String userName)
  {
    try
    {
      if ("a".equals(userName))
      {
        num = 100;
        System.out.println("a set over!");
        Thread.sleep(2000);
      }
      else
      {
        num = 200;
        System.out.println("b set over!");
      }
      System.out.println(userName + " num = " + num);
    }
    catch (InterruptedException e)
    {
      e.printStackTrace();
    }
  }
}

写两个线程分别去add字符串"a"和字符串"b":

public class MyThread13_0 extends Thread
{
  private ThreadDomain13 td;
  
  public MyThread13_0(ThreadDomain13 td)
  {
    this.td = td;
  }
  
  public void run()
  {
    td.addNum("a");
  }
}
public class MyThread13_1 extends Thread
{
  private ThreadDomain13 td;
  
  public MyThread13_1(ThreadDomain13 td)
  {
    this.td = td;
  }
  
  public void run()
  {
    td.addNum("b");
  }
}

写一个主函数分别运行这两个线程:

public static void main(String[] args)
{
  ThreadDomain13 td = new ThreadDomain13();
  MyThread13_0 mt0 = new MyThread13_0(td);
  MyThread13_1 mt1 = new MyThread13_1(td);
  mt0.start();
  mt1.start();
}
//看一下运行结果
a set over!
b set over!
b num = 200
a num = 200

按照正常来看应该打印"a num = 100"和"b num = 200"才对,现在却打印了"b num = 200"和"a num = 200",这就是线程安全问题。我们可以想一下是怎么会有线程安全的问题的:

1、mt0先运行,给num赋值100,然后打印出"a set over!",开始睡觉

2、mt0在睡觉的时候,mt1运行了,给num赋值200,然后打印出"b set over!",然后打印"b num = 200"

3、mt1睡完觉了,由于mt0的num和mt1的num是同一个num,所以mt1把num改为了200了,mt0也没办法,对于它来说,num只能是100,mt0继续运行代码,打印出"a num = 200"

分析了产生问题的原因,解决就很简单了,给addNum(String userName)方法加同步即可:

多线程线synchronized关键字加到方法上

public class ThreadDomain13
{
  private int num = 0;
  
  public synchronized void addNum(String userName)
  {
    try
    {
      if ("a".equals(userName))
      {
        num = 100;
        System.out.println("a set over!");
        Thread.sleep(2000);
      }
      else
      {
        num = 200;
        System.out.println("b set over!");
      }
      System.out.println(userName + " num = " + num);
    }
    catch (InterruptedException e)
    {
      e.printStackTrace();
    }
  }
}

看一下运行结果:

a set over!
a num = 100
b set over!
b num = 200

多个对象多个锁

在同步的情况下,把main函数内的代码改一下:

public static void main(String[] args)
{
  ThreadDomain13 td0 = new ThreadDomain13();
  ThreadDomain13 td1 = new ThreadDomain13();
  MyThread13_0 mt0 = new MyThread13_0(td0);
  MyThread13_1 mt1 = new MyThread13_1(td1);
  mt0.start();
  mt1.start();
}

看一下运行结果:

a set over!
b set over!
b num = 200
a num = 100

这里有一个重要的概念。关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,这里如果是把一段代码或方法(函数)当作锁,其实获取的也是对象锁,只是监视器(对象)不同而已,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁,其他线程都只能呈等待状态。但是这有个前提:既然锁叫做对象锁,那么势必和对象相关,所以多个线程访问的必须是同一个对象。

如果多个线程访问的是多个对象,那么Java虚拟机就会创建多个锁,就像上面的例子一样,创建了两个ThreadDomain13对象,就产生了2个锁。既然两个线程持有的是不同的锁,自然不会受到"等待释放锁"这一行为的制约,可以分别运行addNum(String userName)中的代码。

synchronized(this)锁住的是什么?

锁住的是当前的对象。当synchronized块里的内容执行完之后,释放当前对象的锁。同一时刻若有多个线程访问这个对象,则会被阻塞。

synchronized(object)锁住的什么?

锁住的是object对象。当synchronized块里的内容执行完之后,释放object对象的锁。同一时刻若有多个线程访问这个对象,则会被阻塞。

这里需要注意的是如果object为Integer、String等等包装类时(new出的对象除外),并不会锁住当前对象,也不会阻塞线程。因为包装类是final的,不可修改的,如果修改则会生成一个新的对象。所以,在一个线程对其进行修改后,其他线程在获取该对象的锁时,该对象已经不是原来的那个对象,所以获取到的是另一个对象的锁,所以不会产生阻塞。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 本文向大家介绍聊聊Java并发中的Synchronized,包括了聊聊Java并发中的Synchronized的使用技巧和注意事项,需要的朋友参考一下 1 引言 在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了,本文详细介绍了Java SE1.6中为了减少获得锁

  • 本文向大家介绍Java 同步锁(synchronized)详解及实例,包括了Java 同步锁(synchronized)详解及实例的使用技巧和注意事项,需要的朋友参考一下 Java 同步锁(synchronized)详解及实例 Java中cpu分给每个线程的时间片是随机的并且在Java中好多都是多个线程共用一个资源,比如火车卖票,火车票是一定的,但卖火车票的窗口到处都有,每个窗口就相当于一个线程,

  • 主要内容:示例,死锁解决方案示例死锁描述了两个或多个线程等待彼此而被永久阻塞的情况。 当多个线程需要相同的锁定但以不同的顺序获取时,会发生死锁。 Java多线程程序可能会遇到死锁状况,因为关键字会导致执行线程在等待与指定对象相关联的锁定或监视时出现阻止情况。 看看下面一个例子。 示例 当您编译并执行上述程序时,会出现死锁情况,以下是程序生成的输出 - 上述程序将永久挂起,因为两个线程都不能继续进行,等待彼此释放锁定,所以您可以按

  • 本文向大家介绍详解java并发编程(2) --Synchronized与Volatile区别,包括了详解java并发编程(2) --Synchronized与Volatile区别的使用技巧和注意事项,需要的朋友参考一下 1 Synchronized 在多线程并发中synchronized一直是元老级别的角色。利用synchronized来实现同步具体有一下三种表现形式: 对于普通的同步方法,锁是当

  • 本文向大家介绍详解Java中synchronized关键字的死锁和内存占用问题,包括了详解Java中synchronized关键字的死锁和内存占用问题的使用技巧和注意事项,需要的朋友参考一下 先看一段synchronized 的详解: synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。 一、当两个并发线程访

  • 本文向大家介绍详解java并发之重入锁-ReentrantLock,包括了详解java并发之重入锁-ReentrantLock的使用技巧和注意事项,需要的朋友参考一下 前言 目前主流的锁有两种,一种是synchronized,另一种就是ReentrantLock,JDK优化到现在目前为止synchronized的性能已经和重入锁不分伯仲了,但是重入锁的功能和灵活性要比这个关键字多的多,所以重入锁是