JavaWeb笔记06:Java中的加锁机制(同步机制)——synchronized

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){
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值