深入理解多线程

涉及到:
1.synchronized对象监视器为Object时的使用
2.synchronized对象监视器为Class时的使用
3.非线程安全是如何出现的.
4.关键字volatile的主要作用
5.关键字volatile与synchronized的区别与使用情况

一:同步方法:
(1).非线程安全与线程安全
非线程安全是指多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是"读脏",也就是
取到的数据是被更改过的.
而线程安全是获得实体变量的值是经过同步处理的,不会出现脏读的现象.
(2).方法内的变量为线程安全,也就是方法内部的私有变量是不存在"非线程安全"问题.
(3).实例变量非线程安全,多个线程共同访问一个对象中的实例变量(属性),有可能出现"非线程安全"问题.
在两个线程访问同一个对象中的同步方法时一定是线程安全的.
(4).多个对象多个锁
要先知道synchronized取得的锁是对象锁,也就是多个线程访问同一个对象是同步的(synchronized使用的"对象监视器"是一个),但不同的线程访问不同的对象就不是同步的了.
调用关键字synchronized声明的方法一定是排队运行的,另外不要忘了为什么要加锁,是因为共享,只有共享资源的读写访问才需要同步化.
注意:
A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized方法
A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法则需要等待,也就是同步.
(5).锁重入的问题:
在使用synchronized的时候,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的.也就是说在一个synchronized方法/块的内部调用本类的其他的synchronized方法/块时,是永远可以得到锁的.
可重入锁也支持父子类的继承环境中,也就是可以在子类的synchronized方法中调用父类的synchronized
(6).锁的释放:出现异常的时候会自动释放锁
(7).同步不具有继承性,也就是子类继承了父类的synchronized方法,但是它自己不带synchronized关键字,则该方法不是同步方法,同步方法的前面必须加上synchronized
(8).同步语句块
synchronized方法是对当前对象加锁,而synchronized代码块是对某一个对象进行加锁.
为什么要有同步语句块?是因为synchronized方法存在弊端,就是运行时间比较长,如何解决的?就是使用同步代码块,但是要注意的是这的同步代码块要锁的只是共享资源的读写,不要将整个方法内的内容锁住,当一个线程访问object的一个synchronized同步代码块时,另一个线程可以访问该object对象中的非synchronized(this)代码块.
也就是一半同步,一半异步,不在synchronized块中的就是异步执行,在synchronized块中的就是同步执行.
(9).将任意对象作为对象监视器.
使用synchronized(this)锁定的是当前对象.使用同步方法和同步代码块有两种作用:
1).对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态
2).同一时间只有一个线程可以执行synchronized同步方法或synchronized(this)同步代码块中的代码
使用synchronized(非this对象x)同步代码块,优点:如果一个类中有很多synchroniized方法,这时虽然能实现同步,但会受到阻塞,影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁同步方法争抢this锁,则可大大提高运行效率…
使用synchronized(非this对象x)同步代码块格式进行同步操作时,对象监视器必须是同一个对象,不能是方法内的对象(变量等等).
多个线程调用同一个方法是随机的,什么意思?就是 说某一个线程执行这个方法是同步的,但是多个线程同时多次调用这个方法,会出现两个线程是异步的.
(10).三个结论:
1).当多个线程同时执行synchronized(x){}同步代码块时呈现同步效果
2).当其他线程执行x对象中synchronized同步方法时呈现同步效果
3).当其他线程执行x对象方法里面的synchronized(this)代码块时也呈现同步效果
(11).静态同步synchronized方法与synchronized(class)代码块.
synchronized关键字加到static静态方法上是给Class类上锁,加到非静态方法上是给对象上锁.Class锁可以对类的所有对象实例起作用.,也就是说不同的对象也是同步执行的.
(12).数据类型String的常量池特性:
JVM中具有String常量池缓存的功能.也就是说String a = “a”; String b = “a”; System.out.println(a==b);结果是true,这就导致给String类型加锁的时候,如果String值相同,则两个线程是有相同的锁,因此在大多数情况下,同步synchronized代码块都不使用String作为锁对象,改用其他,比如说new Object()实例化一个Objectt对象,但它并不放入缓存中.
(13).同步synchronized方法无限等待与解决:
同步synchronized方法如果内部有死循环,则会使得线程无限的等待,解决办法是将synchronized方法改为非this同步代码块,使得同步锁锁的不是同一个对象,这样就可以异步执行.
(14).多线程的死锁:
不同的线程都在等待不可能被释放的锁,从而导致所有的任务都无法继续完成,简单的来说就是互相等待,只要互相等待对方释放锁就有可能出现死锁.
在设计程序的时候就要避免双方互相持有对方的锁的情况
(15).volatile关键字:使得变量在多个线程间可见.
作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值.
(16).synchronized与volatile的比较:
1).volatile是线程同步的轻量级实现,只能修饰与变量,而synchronized可以修饰方法以及代码块
2).多线程访问volatile不会发生阻塞,而synchronized会出现阻塞.
3).volatile能保证数据的可见性,但不能保证原子性;synchronized可以保证原子性,也可以间接的保证可见性,因为它会将私有内存和公共内存中的数据同步.
4).关键字volatile解决的是变量在多线程之间的可见性;而synchronized关键字解决的是多线程之间访问资源的同步

猜你喜欢

转载自blog.csdn.net/weixin_42957479/article/details/83317408