Java多线程——synchronized机制

太过爱你忘了你带给我的痛 2021-12-05 17:32 598阅读 0赞

Java多线程——synchronized(内建锁)机制

一、synchronized对象锁相关概念

  学习synchronized机制前先提出几个问题

  为什么要使用synchronized进行同步(加锁)操作?
  synchronized的应用场景及注意事项是什么?

  1、为什么要使用synchronized进行同步(加锁)操作:

   多个线程对同一个对象中的实例变量(所有线程共有的)进行并发访问时,会产生“脏读”,取到的数据是被更改过的。因此使用synchronized进行同步操作,避免”脏读”的出现,保证线程安全。

  2、synchronized的应用场景与注意事项:

   ①只有共享资源的读写访问才需要同步化,如果不是共享资源没有同步的必要。(多个线程有需要共享的东西时,才需要加锁;没有需要共享的东西(每个线程都拥有),就不需要加锁)
   ②多个线程只是在分别调用线程类的run()方法,其他方法或属性是共享的。

  通过进一步的学习提出以下问题

  方法中的变量(局部变量)与实例变量是否线程安全?

  3、方法中的变量(局部变量)与实例变量的线程安全:

   由于局部变量与实例变量的创建时机,局部变量是在被某个线程调用该方法时才会在该线程中为方法中的变量申请空间,因此,局部变量是每个线程所独有的,因此局部变量是线程安全的。而实例变量是在对象创建时就会开辟空间创建,实例变量是所有线程共享的,因此实例变量是线程不安全的。详情请见:局部变量与实例变量的线程安全。

  下面就伴随着问题的产生了解synchronized对象锁的概念。

  synchronized锁住的到底是什么东西?
  synchronized锁住对象后对其他同步/非同步方法(代码块)的影响?
  异常对synchronized锁的影响?
  什么是”脏读”?

  3、synchronized对象锁的概念:

   ①防止多个线程同一时刻执行同一个对象的同步段

  一定要注意是:
      多个线程;
      同一时刻;
      同一对象;
      同步段;

   ②synchronized对象锁的是括号中的对象而非代码。其他线程需要等待当前线程执行完成后释放锁才能再次给该对象加锁。
   ③若线程A持有了Object对象的锁(通过同步方法/同步代码块获取),线程B不能再访问Object对象的同步方法/同步代码块,但完全可以异步调用非同步方法/块
   ④若一个线程执行的代码出现异常,其所持有的锁就会自动释放。(抛出异常会释放锁
   ⑤脏读的深入理解

二、synchronized机制相关实现

  1、对象锁与全局锁

   ①对象锁:synchronized(object)、synchronized(this)、以及普通的synchronized方法,锁的是括号中的对象而非代码。
  1. public synchronized void method(){}
  2. synchronizedObject){}
  3. synchronizedthis){}

   对象锁中:即便这个对象的某些属性发生改变,但只要对象没有改变,不同线程之间还是同步状态。

   ②全局锁:使用类的静态同步方法(static synchronized),或在同步代码块中锁当前的Class对象,此时锁的是当前使用的类(代码段)而非对象(其实锁住的还是对象,只不过是Class对象,而非该对象本身)
  1. public static synchronized void method(){}
  2. synchronized(类名称.class){}

  2、同步方法与同步代码块

   同步代码块与同步方法到底是什么?
   synchronized(任意对象)详解
   synchronized(任意对象)中为什么不推荐使用含常量池的对象?
   同步代码块与同步方法的作用是什么?
   为什么多推荐使用同步代码块而不是同步方法?

   ①同步代码块与同步方法相关概念:
    同步代码块:在方法中使用synchronize(对象){},表示同一时刻只能有一个线程能够进入同步代码块,但是多个线程可以同时进入方法
    同步代码块分类:
     a)synchronized(this){}—该类的对象
     b)synchronized(类名.class)-全局锁{}—类反射对象
     c)synchronized(任意对象,obj);—任意对象都有markword字段
    同步方法:在方法声明上加synchronize,表示此时同一时刻只能有一个线程能够进入同步方法
    同步方法分类
     a)普通方法   //相当于 this
     b)静态方法-全局锁    //相当于 类名.class
   ② synchronized(任意对象)同步代码块详解
    此时的任意对象为非this对象,将该”任意对象”作为”对象监视器”实现同步的功能。
    a)当多个线程持有的”对象监视器”为同一个对象时,synchronized(x){}同步代码块时呈同步效果。(同一时间只有一个线程可以执行synchronized(x)同步代码块中的代码),若不是同一个对象就是异步执行了。
    b)当其他线程执行x对象中synchronized同步方法时呈同步效果(x对象本身也有同步方法)
    c)当其他线程执行x对象方法里面的synchronized(this)代码块时也呈同步效果
    d)当其他线程调用不加synchronized关键字的普通方法时,还是异步调用。
    使用任意对象(非this)的好处在于:若一个类中有很多个同步方法,这时虽然能实现同步,但多个线程在调用不同synchronized方法时会出现阻塞,而synchronized(非this对象)只要括号内不是同一个对象,执行就是异步的(eg:不会与其他this同步方法争抢this锁),大大提高运行效率。
   ③ 由于Java中常量池的存在,一般synchronized(任意对象)中的任意对象不使用存在常量池的对象。
    eg:由于String存在常量池,所以一般synchronized(x)中x不用String对象。
  1. eg
  2. print中有以String(传入的参数)为锁的代码块
  3. 线程1service.print("AA")
  4. 线程2service.print("AA")

  解析:由于”AA”是匿名对象,在不同的线程中相当于new了一个新的对象,相当于不同的线程拥有不同的对象,按道理这两个线程锁了两个不同的对象,这两个线程之间是异步的,但由于String常量池的存在,这两个对象在内部实际是一个对象,则导致线程2不能访问代码块了。

   ④同步代码块与同步方法的作用:
    多个线程调用同一个对象的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果是按顺序执行,也就是同步的、阻塞的。
    a)synchronized同步方法对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。并且,同一时间只有一个线程可以执行synchronized同步方法中的代码。
    b)synchronized(this)同步代码块对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。并且,同一时间只有一个线程可以执行synchronized同步方法中的代码。
   ⑤为什么多推荐使用同步代码块而不是同步方法
    同步方法存在弊端,若该同步方法执行一个长时间的任务,则当某个线程线程执行该方法时,其他线程受到阻塞需要等待比较长的时间,效率低下。此时可以采用同步代码块的方式,将不重要的操作(不操作共享变量且比较耗时)设置为异步执行,放在synchronized代码块外,而将需要同步执行的操作放在synchronized代码块内,实现同一个方法中一半异步,一半同步的策略,大大提高效率。当然,若比较耗时的是需要同步执行的操作,同步方法与同步代码块的使用效率基本相同(同步代码块效率还是更高点,毕竟一个方法内不是所有操作都是需要同步进行的)。
    同步方法还存在一个弊端,由于同步方法锁的都是当前对象this,也就是说若线程A执行某个同步方法时,线程B不能执行该类中其他同步方法(需要等A释放该对象锁),这就导致了,如果一个类中有很多个synchronized方法,多个线程在调用不同synchronized方法时会出现阻塞的情况,效率会很低。因此可以使用同步代码块(任意对象)的方式,将这些同步方法按照功能或其他标准分类,每一部分对应一个对象(同一个对象)加锁,不同对象的监视器之间是异步的,这样就可以大大提高效率。

发表评论

表情:
评论列表 (有 0 条评论,598人围观)

还没有评论,来说两句吧...

相关阅读

    相关 线 -锁机制 - Synchronized

    使用多线程主要是为了提高系统资源利用率,但由于多线程之间是并发执行,且系统调度又是随机的,多线程环境下业务中一般会配置多个线程执行相同的代码,如果在此段代码中存在共享变量或一些

    相关 Java线的同步机制synchronized

    如果程序是单线程的,就不必担心此线程在执行时被其他线程“打扰”,就像在现实世界中,在一段时间内如果只能完成一件事情,不用担心做这件事情被其他事情打扰。但是,如果程序中同时使用多