1.synchronized—对象加锁
synchronized方法包括两种,一是标注了synchronized关键字的方法,一种是synchronized代码块.而不论是同步代码块还是同步方法都具有了原子性和可见性.
1.1 原子性
原子性指的是一个时刻,只能有一个线程执行一段同步代码或一个同步方法,这个同步代码段或这个同步方法会通过一个monitor object保护.
作用:防止多个线程在更新共享时相互冲突.
原理:对象监视器(锁),只有获取到监视器的线程才能继续执行,否则线程会等待获取监视器,java中每个对象或者类都有一把锁与之对应,对于对象来说,监视的是这个对象的实例变量,对于类来说,监视的是类变量(一个类本身是类Class的对象,所以与类关联的锁也是对象锁),当线程执行到同步代码块或同步方法时,JVM会锁住这个引用对象,当离开时才会释放这个引用对象上的锁.对象锁是JVM内部机制,锁的获取黑盒释放是JVM完成的.
1.2 可见性
可见性会消除内存缓存和编译器优化的各种反常行为,也就是说它必须保证释放锁之前对共享数据做出的更改对于随后另一个获得此锁的线程是可见的.
作用:如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是另一个线程修改前的值或不一致的值.
原理:当对象获取锁时,它首先使自己的高速缓存无效,这样就可以保证直接从主存中读取变量的值,同样,在对象释放锁之前,它会刷新告诉缓存,强制使做的任何修改都出现在主存中,这样,就能保证在同一个锁上同步的两个线程在synchronized块修改的变量的相同值.
1.3为什么要使用同步
- 读取某个变量的值可能已经被另一个线程修改过.
- 写入的某个变量的值可能马上被另一个线程读取.
1.4synchronized的限制
synchronized虽然使用简单,但是它的功能有限制.
* 它无法中断一个正在等待获锁的线程.
* 也无法通过投票得到锁,如果不想等下去,也就没法得到锁;
* 同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况。
1.5synchronized例子
用synchronized实现1-9的打印,要求,线程A打印123,然后线程B打印456,然后线程A打印789. 具体代码如下:
package thread;
/**
* Created by yang on 16-7-10.
*/
public class WaitNotifyDemo {
private volatile int val = 1;
private synchronized void printAndIncrease() {
System.out.println(Thread.currentThread().getName() + " prints " + val);
val++;
}
// print 1,2,3 7,8,9
public class PrinterA implements Runnable {
@Override
public void run() {
while (val <= 3) {
printAndIncrease();
}
// print 1,2,3 then notify printerB
//WaitNotifyDemo.this代表的是WaitN