0 synchronized
利用synchronized实现同步的基础:Java中的每一个对象 都可以作为锁。
- 对于普通同步方法,锁的是当前实例对象。
- 对于静态同步方法,锁的是当前类的Class对象。
- 对于同步代码块,锁的是Synchronized括号中的对象。
(1) synchronized的原理:
JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。方法同步和代码块同步(代码块同步依靠方法修饰符上的ACC_SYNCHRONIZED来完成)本质上都是使用monitorenter和monitorexit指令来实现的,但是具体实现有一些小区别。
拿代码块同步举例,monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM保证每个monitorenter必须有对应的monitorexit与之对应。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。
synchronized用到的锁在Java对象头中:Mark Word中默认存储对象的HashCode、分代年龄和锁标记位。下面是Java对象头的长度:
互斥锁的特性:
- 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程的协调机制,这样在同一时间只有一个线程对需要同步的代码块进行访问。互斥性也称为操作的原子性。
- 可见性:必须保证在锁释放之前,对共享变量所做的修改,对于随后获得该锁的线程是可见的,否则另一个线程可能是在本地缓存的某个副本上进行操作,从而引起不一致。
注意:
synchronized锁的是对象。
1 作为方法修饰符,写在定义的方法之前。
1)同步方法:synchronized修饰普通方法,锁的是this
引用指向的对象
synchronized void method(){
}
2)同步静态方法:synchronized修饰静态方法,锁的是 类名.class
引用指向的对象(class本身也有一个对象,可以通过类名.class引用找到这个对象(反射用法))
synchronized static void method(){
}
2 作为代码块
1)同步代码块:锁的是引用指向的对象(对象里面的锁)
void method(){
Object a = new Object();
synchronized(一个引用){
}
}
3 等价关系
1)修饰静态方法等价于静态代码块
静态方法:
class A{
synchronized static void method(){}
}
静态代码块:
class A{
static void method(){
synchronized(A.class){}
}
}
2)修饰普通方法等价于普通代码块
普通方法
synchronized void function{}
普通代码块:
void function(){
synchronized(this){}
}
对象: 对象是对一块堆内存区域的抽象, 对象中保存了各种属性。
注意:
- JVM实现时,每个对象都带有一把锁,每个对象都可以作为一把锁 (Monitor Lock)——(监视器 /同步锁 /监视器锁)。
例如:Object o = new Object(); //o这个引用指向的对象中,拥有一把锁 - 线程抢锁失败后,必须让出CPU,并再也没有资格抢锁,状态从Runnable变成Blocked(专为synchronized抢锁失败设计的状态), 从就绪队列移到阻塞队列。
- 当线程执行unlock时,会把锁释放掉,并把当时因抢锁失败的线程从Blocked改为Runnable, 使其重新拥有了抢锁的资格,从阻塞队列移到就绪队列中。
- synchronized需要用到对象中的锁,所以也是一个解引用的过程
Object a = null;
synchronized(a){
//肯定会抛NullPointerException
}
4 多线程互斥的必要条件:
多线程间有抢同一把锁的行为,线程之间如果抢的是同一把锁(引用都指向同一个对象)就会产生互斥;
public class Main {
static long n = 0;
static long COUNT = 1000_0000;
static Object o = new Object();//o这个对象作为锁
static class Add extends Thread {
@Override
public void run() {
for (long i = 0; i < COUNT; i++) {
synchronized (o) {
n++;
}
}
}
}
static class Sub extends Thread {
@Override
public void run() {
for (long i = 0; i < COUNT; i++) {
synchronized (o) {
n--;
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Add add = new Add();
Sub sub = new Sub();
add.start();
sub.start();
add.join();
sub.join();
System.out.println(n);//0
}
}
抢的不是同一把锁,不互斥的情况:
A线程运行A方法,B线程运行B方法,因为A线程锁的是类对象,B线程锁的是当前对象,所以A,B两个线程不互斥。
synchronized static void A(){
}
void B(){
synchronized(this){
}
}