对象及变量的并发访问

来源:互联网 发布:青岛西海岸交易软件 编辑:程序博客网 时间:2024/06/11 04:05

掌握如下关键技术点:

  • synchronized对象监视器为Object时的使用
  • synchronized对象监视器为Class时的使用
  • 非线程安全是如何出现的
  • 关键字volatile的主要作用
  • 关键字volatile与synchronized的区别及使用情况

synchronized同步方法

  • 方法内的变量为线程安全

"非线程安全"问题存在于"实例变量"中,如果是方法内部的私有变量则不存在"非线程安全"问题(线程安全)
原因:方法内部是私有的特性

  • 实例变量非线程安全

如果多个线程共同访问1个对象中的实例变量,则有可能出现"非线程安全"问题--可能出现覆盖的情况
用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况
结论:在两个线程访问同一个对象中的同步方法时一定是线程安全的.

  • 多个对象多个锁

两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是以异步的方式运行的.
原因:关键字synchronized取得的锁都是对象锁,而不是把一段代码或者方法(函数)当做锁,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象
但如果多个线程访问多个对象,则JVM会创建多个锁.
同步:synchronized 异步asynchd

  • synchronized方法与锁对象

调用关键字synchronized声明的方法一定是排队运行的,只有共享资源的读写访问才需要同步化,如果不是共享资源,那么根本没有同步的必要

结论:

1.A线程先持有object对象的Lock锁,B线程可以以异步的方法调用object对象中的非synchronized类型的方法 2.A线程先持有object对象的Lock锁,B线程如果在这是调用object对象中的synchronized类型的方法则需要等待,也就是同步

  • 脏读

脏读(dirtyRead):虽然赋值时进行了同步,但在取值时有可能出现一些意想不到的意外--在读取实例变量时,此值已经被其他线程更改过了。
解决方法:添加synchronized关键字

  • synchronized锁重入

在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。

在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。

"可重入锁"的概念:自己可以再次获得自己的内部锁。比如有1条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获得这个对象的锁的时候还是可以获取的,如果不可重入锁的话,就会造成死锁。
当存在父子类继承关系时,子类是完全可以通过"可重入锁"调用父类的同步方法的

  • 出现异常,锁自动释放

当一个线程执行的代码出现异常时,其所持有的锁会自动释放

  • 同步不具有继承性

同步不可以继承

synchronized同步语句块

synchronized方法是对当前对象进行加锁,而synchronized代码块是对某一个对象进行加锁.

  • synchronized方法的弊端

用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长的时间.在这样的情况下可以使用synchronized同步代码块来解决.

  • synchronized同步代码块的使用

当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块.

  • 用同步代码块解决同步方法的弊端

当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。
优点:时间缩短,运算效率加快,synchronized同步代码块做到真正的同步

  • 一半异步,一半同步

不在synchronized代码块中的代码是异步执行,在synchronized块中就是同步执行

  • synchronized代码块间的同步性

在使用同步synchronized(this)代码块时需要注意的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说synchronized使用的"对象监视器"是一个.

  • 验证同步synchronized(this)代码块是锁定当前对象的

和synchronized方法一样,synchronized(this)代码块也是锁定当前对象的

  • 将任意对象作为对象监视器

多个线程调用同一个对象中的不同名称的synchronized同步方法或者synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的.
synchronized同步方法和synchronized(this)同步代码块的作用:
1.对其他synchronized同步方法或者synchronized(this)同步代码块调用呈阻塞状态;
2.同一时间只有一个线程可以执行synchronized同步方法(同步代码块)中的代码
synchronized(非this对象x)同步代码块的作用:
1.在多个线程持有"对象监视器"为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码
锁非this对象具有一定的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率;但是如果使用同步代码块锁非this对象,则异步的,不与其他锁this同步方法争抢this锁,则可以大大提高运行效率.
注意:"synchronized(非this对象x)同步代码块"格式中对象监视器必须是同一个对象.如果不是同一个对象监视器则是异步调用,出现交叉运行
多个线程调用一个方法是随机的,可能出现脏读。

  • 细化验证3个结论

1.当多个线程同时执行synchronized(x){}同步代码块时呈同步效果
2.当其他线程执行x对象中synchronized同步方法时呈同步效果
3.当其他线程执行x对象方法不加synchronized关键字的方法时也呈现同步效果
注意:如果其他线程调用不加synchronized关键字的方法时,还是异步调用

  • 静态同步synchronized方法与synchronized(class)代码块

synchronized关键字加到static静态方法上是给Class类上锁,
而synchronized关键字加到非static静态方法上是给对象上锁
Class锁可以对类的所有实例起作用
同步synchronized(class)代码块的作用其实和synchronized static方法的作用是一样的

  • 数据类型String的常量池特性

在JVM中具有String常量池缓存的功能
大多数情况下,同步synchronized代码块都不使用String作为锁对象,而改用其他的,比如new Object()实例化一个Object对象,但它并不放入缓存中

  • 同步synchronized方法无限等待与解决

使用同步代码块(非this对象x)的方式解决同步方法造成的死循环

  • 多线程死锁

不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成,死锁会造成线程的"假死"
JDK自带工具来检测是否有死锁的现象
只要出现相互等待对方释放锁就有可能出现死锁

  • 内置类与静态内置类

  • 内置类与同步:实验1

在内置类中有两个同步方法,但是用的却是不同的锁(对象监视器不同),打印的结果也是异步的

  • 内置类与同步:实验2

同步代码块synchronized(class2)对class2上锁后,其他线程只能以同步的方式调用class2中的静态同步方法

  • 锁对象的改变

在将任何数据类型作为同步锁时,需要注意的是,是否有多个线程同时持有锁对象,如果同时持有相同的锁对象,则这些线程之间就是同步的;如果分别获得锁对象,这些线程之间就是异步的.
提示:只要对象不变,即使对象的属性被改变,运行的结果还是同步的

volatile关键字

关键字volatile的主要作用是使变量在多个线程间可见

  • 关键字volatile与死循环

  • 解决同步死循环

关键字volatile的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值

  • 解决异步死循环

私有堆栈的值与公共堆栈中的值不同步,解决这样的问题需要使用volatile关键字,强制性从公共堆栈中进行取值.
关键字synchronized和volatile比较:
1.关键字volatile是线程同步的轻量级实现,所以volatile性能比synchronized好,并且volatile只能修饰于变量,而synchronized可以修饰方法以及代码块.随着JDK新版本的发布,synchronized关键字在执行效率上得到了很大的提升,在开发中使用synchronized关键字的比率还是比较大的.
2.多线程访问volatile不会发生阻塞,而synchronized会出现阻塞
3.volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步.
4.关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的是多个线程之间访问的资源的同步性
线程安全包含原子性可见性两个方面

  • volatile非原子的特性

关键字volatile虽然增加了实例变量在多个线程之间的可见性,但是它却不具备同步性,那么也就不具备原子性
关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新值使用
如果修改实例变量中的数据,比如i++,这样的操作并不是一个原子操作,是非线程安全的.
i++操作步骤分解如下:
1.从内存中取出i的值;
2.计算i的值
3.将i的值写到内存中
假如在第2步计算值的时候,另一个线程也修改i的值,那么这个时候就会出现脏数据,解决办法:使用synchronized关键字.
volatile本身并不处理数据的原子性,而是强制对数据的读写及时影响到主内存的
变量中内存中工作的过程:
1.read和load阶段:从主内存复制变量到当前线程工作内存
2.use和assign阶段:执行代码,改变共享变量的值
3.store和write阶段:用工作内存数据刷新主存对应变量的值
对于多个线程访问同一个实例变量还是需要加锁同步

  • 使用原子类进行i++操作

可以使用AtomicInteger原子类进行实现
原子操作是不能分割的整体,没有其他线程能够中断或检查正在原子操作中的变量,一个原子(atomic)类型就是一个原子操作可用的类型,它可以在没有锁的情况下做到线程安全(thread-safe)

  • 原子类也并不完全安全

原子类在具有逻辑性的情况下输出的结果也是具有随机性的
原子类的方法时原子的,但是方法与方法之间不是原子的,解决方法:使用同步。

  • synchronized代码块有volatile同步的功能

关键字synchronized可以使多个线程访问同一个资源具有同步性,而且它还具有将线程工作中的私有变量与公共内存中变量同步的功能。
特征:互斥性可见性
总结:
有效控制线程间处理数据的顺序性
对处理后的数据进行有效值的保证
原创粉丝点击