多线程学习笔记--02(对象及变量的并发访问)

  1.学习目标    

     Synchronized对象监视器为Object时的使用

     Synchronized对象监视器为Class时的使用

     非线程安全是如何出现的,

     关键字volatile的主要作用

     关键字Synchronized和volatile的主要区别以及使用情况

   2. Synchronized对象监视器为Object时的使用

     1.synchronized可以修饰方法,两个线程访问同一个对象(这个对象只new了一次)中的同步方法的时候一定是线程安全的,即按照顺序执行的,也就是说A线程执行完了再执行B线程。如果两个线程访问同一个类的不同的两个实例(即便他的方法是同步的,这会创建两个锁),这其实数据不共享了,不会出现线程安全的问题,两个线程是异步执行的。为什么他们是异步执行?这就说明synchronized取得的锁是对象锁,而不是把一段代码或者方法当作锁。需要注意的是:方法中的变量不存在线程安全的问题,永远都是线程安全的,这是方法内部的私有变量的特性造成的。

    总结:

      A线程先持有object对象的Lock锁(就是A线程调用object的同步方法),这时候B线程可以以异步的方式调用object对象中的非synchronized类型的方法。

      如果A线程先持有object对象的Lock锁(就是A线程调用object的同步方法),B线程这时候再调用object对象中的synchronized类型的方法时则需等待。

    脏读:发生脏读的情况是在读取实力变量时,此值已经被其他线程修改过了。即便赋值的方法是同步的,取值的方法不同步也会出现脏读的情况,解决办法当然是同步。

    Synchronized锁重入:自己可以再次获取自己的内部锁,比如有一条线程获得了某个对象锁,此时这个对象锁还没有被释放,当其再次想要获取这个对象的锁的时候还是可以获取的。比如在一个类中,一个线程同步的方法可以调用另一个线程同步的方法。如果不可重入锁的话就会造成死锁。需要注意的是:可重入锁也支持在父子类继承的环境中,当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法。需要注意的是:这里是可重入锁支持继承,而不是同步支持继承,锁具有继承性是指子类调用父类的同步方法,而同步不具有继承性是子类重写父类的同步方法不具有同步的效果。

    2.synchronized同步代码块的使用,Synchronized方法存在的弊端,如果A线程需要执行一个长时间的任务,那么B线程必须等待很长的时间,用同步代码块可以解决这个问题。因为synchronized方法是对当前对象进行加锁,而synchronized代码块是对某一个对象(某一个对象可以是this,非this等等)进行加锁。

     使用同步代码块的好处当然是可以弥补同步方法的缺陷,即:使用同步代码块来加快同步方法的执行时间,就是把可能出现线程不安全的赋值操作等等用synchronized(this)包裹起来。因为当一个线程访问object的一个synchronized同步代码块时。另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。如果同步方法出现无限等待的问题,就是如果一个线程调用的同步方法陷入一个死循环,其他线程永远没有机会执行该对象的其他同步方法。有没有解决的办法呢?当然有,就是把同步方法换成同步代码块代替,这样进入方法的的时候就不会被阻塞。

    总结:

    同步代码块和同步方法的作用:对其他synchronized方法或synchronized(this)同步代码块调用呈阻塞状态。同一时间只有一个线程可执行同步代码块或同步方法。

   synchronized不仅可以锁this对象,可以将任意对象作为对象监视器,这个任意对象大多数是实例变量及方法的参数,使用的格式为synchronized(非this对象),当然多个线程持有对象监视器持为同一个对象的前提下(必须是同一个锁),同一时间只有一个线程可执行synchronized(非this对象)同步代码块中的代码。

   锁非this对象相比锁this对象和同步方法的好处,如果在一个类中有很多个synchronized方法,这时虽然能够实现同步,但是会受到阻塞,所以会影响运行效率,但是使用同步代码块锁非this对象。synchronized(非this)代码块中的程序与同步方法是异步的,不会与其他锁this同步方法争抢this锁,则可大大提高运行效率。再次强调:对象监视器必须监视的是同一个对象(synchronized锁的是同一个对象)如果监视的不是同一个对象,运行的结果就是异步了。

   同步代码块放在非同步的方法中进行声明,并不能保证调用方法的线程的执行同步,也就是说线程调用方法的顺序是无序的。虽然在同步块中执行的顺序是同步的,这样极易出现脏读的问题。

3.Synchronized对象监视器为Class时的使用

  synchronized作用在静态方法上相当于对当前.java文件所对应的Class类进行持锁。而synchronized关键字加到非静态方法是给对象上锁。他们有什么区别呢?由于类是由Class文件生成,如果对Class文件上锁,那么对类的所有实例起作用。

4.数据类型String的常量池特性

  因为JVM中具有String常量池的缓冲功能,所以String a=”a” String b=”a”这样的话a和b是完全相等的。如果此时用synchronized锁住这两个相同的字符串,当多个线程传入相同的字符串进去的时候,只会有一个线程被执行,因为锁是一样的,最好的是锁一个new Object的对象,这样拿到的是不同锁,线程异步执行。

最终总结:在将任何数据类型作为同步锁时,需要注意的是,是否有多个线程同时持有锁对象。如果同时持有相同的锁对象,则这些线程之间就是同步的。如果分别获得锁对象,则这些线程之间就是异步的。

5.关键字volatile的主要作用

   关键字volatile主要作用是使变量在多个线程之间可见,关键字volatile的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。

线程启动的时候线程的变量会存在于公共堆栈及线程的私有堆栈中。在JVM被设置为-server模式时为了线程的效率,线程一直在私有堆栈中拿值,普通情况下线程修改变量的值只是修改公共堆栈的值,而私有堆栈的值仍未改变,所以解决这个问题的办法就是用volatile修饰变量的值。迫使线程去从公共堆栈取值,达到变量在多个线程之间可见的一个效果。但是volatile最致命的缺点是不支持原子性。

  如何理解volatile不支持原子性,比如volatile修饰的变量i去执行++i,--i等运算的时候,当多个线程来执行这个操作的时候还是会出现不同步的情况,解决的办法当然是使用synchronized来关键字。

  Synchronizedvolatile 的区别?

  1. 首先要明白的一点是volatile解决变量在多个线程之间的可见性。而synchronized是解决多个线程之间访问资源的同步性。
  2. Volatile是线程同步的轻量级实现,效率要比synchronied要好,但是volatile只能修饰变量,而synchronized可以修饰方法和代码块。
  3. 多线程访问volatile不会发生阻塞,而synchronized会发生阻塞。
  4. Volatile能保证数据的可见性,但是不能保证数据的原子性。Sychronized既可以保证原子性也可以保证可见性,因为他会将私有内存和公共内存的数据做同步。

 Volatile的本质是JVM虚拟机只是保证从主内存加载到线程工作内存的值是最新的。

   

      

 

 

    

猜你喜欢

转载自blog.csdn.net/weixin_37650458/article/details/84673107