Synchronized关键字详解

synchronized是Java提供的一个并发控制的关键字,作用于对象上。主要有两种用法,分别是同步方法和同步代码块,保证了代码的原子性和可见性以及有序性,但是不会处理重排序以及代码优化的过程。

//同步方法
    public synchronized void sms() throws InterruptedException {
        System.out.println("Sms....");
    }
   public static void main(String[] args) {
        while (true){
        //同步代码块
            synchronized ("obj"){
                ......
            }
        }
    }

在同步方法中,Synchronized关键字实现同步的原因是使用了flag标记ACC_SYNCHRONIZED,当调用同步方法时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否设置,设置了,执行线程会先持有同步锁,然后执行方法,最后方法完成释放锁。

用以下代码测试一下,看看字节码指令

public class A {
    public static synchronized void test(){    
    }
    public static void main(String[] args) {
        test();
    }
}

字节码指令如下

//由于太多,部分省略,直接看下面
  public static synchronized void test();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED   //标志位
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 4: 0
        .....

而在同步代码块中,Synchronized关键字实现同步的原因是使用了monitorentermonitorexit 来进行同步处理。

public class A {
    public static void main(String[] args) {
        synchronized ("obj"){

            }
    }
}

字节码指令如下

//由于太多,部分省略,直接看下面
public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // String obj
         2: dup
         3: astore_1
         4: monitorenter        //看这里
         5: aload_1
         6: monitorexit         //还有这里
         7: goto          15
        10: astore_2
        11: aload_1
		.......

Synchronized关键字还具有锁重入功能

public class Test06 {

    public static void main(String[] args) {
            new Service().a();
    }
}
class Service{
    synchronized void a(){
        System.out.println("a");
        b();
    }
    synchronized void b(){
        System.out.println("b");
        c();
    }
    synchronized void c(){
        System.out.println("c");
        d();
    }
    synchronized void d(){
        System.out.println("d");
    }
}

在使用synchronized关键字时,当一个线程得到一个对象锁后,再次请求该对象锁是可以得到该对象锁的。简单来说,在一个synchronized方法/块的内部调用本类的其它synchronized方法/块,是可以得到锁的。

锁重入也支持继承环境,看看下面的示范

/**
 * @Auther: 罗罗
 */
public class Test06 {

    public static void main(String[] args) {
        new Mythread().start();
    }
}
class Father{
    int i = 10;
    synchronized void f(){
        System.out.println("father"+i--);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class Child extends Father{
    synchronized void c(){
        while (i > 0){
            System.out.println("child"+i--);
            try {
                Thread.sleep(1000);
                super.f();
            } catch (InterruptedException e) {

            }
        }
    }
}
class Mythread extends  Thread{
    @Override
    public void run() {
        new Child().c();
    }
}
"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar=53208:D:\IntelliJ IDEA\IntelliJ IDEA 2018.2.4\bin" -"Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program bin\rep\org\apache\xmlbeans\xmlbeans\3.0.1\xmlbeans-3.0.1.jar;D:\apache-maven-3.5.4-bin\rep\joda-time\joda-time\2.10.5\joda-time-2.10.5.jar" Test06
child10
father9
child8
father7
child6
father5
child4
father3
child2
father1

Process finished with exit code 0

可以看出,子类完全可以通过锁重入调用父类的同步方法的

出现异常,Synchronized锁也会自动释放

public class Test06 {
    public static void main(String[] args) {
        Father father = new Father();
        new Mythread1(father).start();
        new Mythread2(father).start();
    }
}
class Father{
    int i = 10;
    synchronized void f(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("f "+i--);
    }
    synchronized void f1(){
        int c =2/0;
        System.out.println("f1 "+c);
    }
}
class Mythread1 extends  Thread{
    private Father father;
    public Mythread1(Father father){
        this.father = father;
    }
    @Override
    public void run() {
        father.f1();
    }
}
class Mythread2 extends  Thread{
    private Father father;
    public Mythread2(Father father){
        this.father = father;
    }
    @Override
    public void run() {
       father.f();
    }
}
"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar=53587:D:\IntelliJ IDEA\IntelliJ IDEA 2018.2.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program time\2.10.5\joda-time-2.10.5.jar" Test06
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
	at Father.f1(Test06.java:26)
	at Mythread1.run(Test06.java:46)
f 10

Process finished with exit code 0
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗罗的1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值