深入理解Java中的synchronized关键字

目录

前言

一、什么是synchronized

二、synchronized的底层实现

三、synchronized与其他同步机制的比较

四、synchronized的使用方式

1. synchronized的重入

2.synchronized的异常


前言

        Java是一种面向对象的编程语言,以其强大的并发处理能力而闻名。在多线程编程中,保证线程安全是一个重要的问题。本文将详细探讨Java中的一个关键机制——synchronized关键字,帮助你深刻理解其概念、使用方法及其背后的工作原理。

一、什么是synchronized

   synchronized是Java提供的一种内置同步机制,用于解决多线程环境下的并发问题。当多个线程同时访问共享资源时,可能会出现数据不一致的情况。synchronized通过锁(monitor)的方式来确保同一时间只有一个线程可以访问被保护的代码块,从而避免数据的竞争问题。

二、synchronized的底层实现

        Java中synchronized关键字是通过进入和退出Monitor(监视器)对象来实现的。每个对象都有一个隐式的监视器锁。当我们使用synchronized同步方法或同步代码块时,线程必须先获得该锁才能进入同步区域。

        在JVM内部synchronized是通过字节码指令monitorentermonitorexit来实现的。当线程执行到synchronized区域时,它会尝试获取对象的监视器锁。若成功,则进入同步区域;否则,线程会被阻塞,直到获得锁为止。

三、synchronized与其他同步机制的比较

除了synchronized之外,Java还提供了其他一些高级的同步机制,如ReentrantLockSemaphore等。以下是它们的一些比较:

  • 可重入性synchronizedReentrantLock都是可重入锁。可重入性意味着同一个线程可以多次进入同步代码块而不会被自己阻塞。

  • 灵活性ReentrantLocksynchronized更加灵活。它提供了更多的方法,例如lockInterruptibly()tryLock()等,可以更精细地控制锁的行为。

  • 性能:在较早版本的Java中,synchronized的性能表现较差。但从Java 6开始,JVM对synchronized做了诸多优化,如偏向锁、轻量级锁以及自旋锁等,使其性能有了显著提升。

  • 条件变量ReentrantLock提供了Condition类,可以实现多个等待队列,配合await()signal()方法实现更加复杂的线程间通信。而synchronized只能依赖于wait()notify()notifyAll()方法来管理线程间的通信。

四、synchronized的使用方式

        关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时是可以再次得到该对象的锁。出现异常,锁自动释放。

1. synchronized的重入

package com.ctb.sync5;
/**
 * synchronized的重入
 * 
 * @author biao
 *
 * 2024年
 */
public class SyncDubbo1 {
​
    public synchronized void method1(){
        System.out.println("method1..");
        method2();
    }
    public synchronized void method2(){
        System.out.println("method2..");
        method3();
    }
    public synchronized void method3(){
        System.out.println("method3..");
    }
    
    public static void main(String[] args) {
        final SyncDubbo1 sd = new SyncDubbo1();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                sd.method1();
            }
        });
        t1.start();
    }
}

结果: 

注:synchronized的重入,在同时添加都synchronized锁后,在得到一个对象的锁后是可以再次得到该对象的锁的

package com.ctb.sync5;
/**
 * synchronized的重入
 * 
 * @author biao
 *
 * 2024年
 */
public class SyncDubbo2 {
​
    static class Main {
        public int i = 10;
        public synchronized void operationSup(){
            try {
                i--;
                System.out.println("Main print i = " + i);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    static class Sub extends Main {
        public synchronized void operationSub(){
            try {
                while(i > 0) {
                    i--;
                    System.out.println("Sub print i = " + i);
                    Thread.sleep(100);      
                    this.operationSup();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                Sub sub = new Sub();
                sub.operationSub();
            }
        });
        
        t1.start();
    }
    
    
}
​

注:也可以通过继承的方式进行synchronized的重入

2.synchronized的异常

        对于web应用程序,异常释放锁的情况,如果不及时处理,很可能对你的应用程序业务逻辑产生严重的错误,比如你现在执行一个队列任务,很多对象都在去等待第一个对象正确执行完毕再去释放锁,但是第一个对象由于异常的出现,导致业务逻辑没有正常执行完毕。所以这一点一定要引起注意,在编写代码的时候,一定要考虑周全。

package com.ctb.sync5;
/**
 * synchronized的异常
 * 
 * @author biao
 *
 * 2024年
 */
public class SyncException {
​
    private int i = 0;
    public synchronized void operation(){
        while(true){
            try {
                i++;
                Thread.sleep(200);
                System.out.println(Thread.currentThread().getName() + " , i = " + i);
                if(i == 10){
                    Integer.parseInt("a");
                    //throw new RuntimeException();
                }
            } catch (Exception e) {//InterruptedException
                e.printStackTrace();
                System.out.println("log info i = " + i);
                //throw new RuntimeException();
                //continue;
            }
        }
    }
    
    public static void main(String[] args) {
        
        final SyncException se = new SyncException();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                se.operation();
            }
        },"t1");
        t1.start();
    }
    
    
}

结果: 

注:在我们运行数据时,当它某条数据出现错误时,继续往下执行,并记录异常数据,后续根据日志去解决问题。

当然我们也可以在异常产生时进行停止数据的变动,

第一种:抛出运行时异常

public synchronized void operation(){
        while(true){
            try {
                i++;
                Thread.sleep(200);
                System.out.println(Thread.currentThread().getName() + " , i = " + i);
                if(i == 10){
                    Integer.parseInt("a");
//                  throw new RuntimeException();
                }
            } catch (Exception e) {//InterruptedException
                e.printStackTrace();
                System.out.println("log info i = " + i);
                throw new RuntimeException();
//              continue;
            }
        }
    }

结果: 

        第二种:InterruptedException 是 Java 中的一个受检查异常,表示一个线程被中断。当一个线程调用 Thread 类的 interrupt() 方法时,会给该线程设置一个中断标记,表示该线程被中断了。

public synchronized void operation(){
        while(true){
            try {
                i++;
                Thread.sleep(200);
                System.out.println(Thread.currentThread().getName() + " , i = " + i);
                if(i == 10){
                    Integer.parseInt("a");
//                  throw new RuntimeException();
                }
            } catch (InterruptedException e) {//InterruptedException
                e.printStackTrace();
                System.out.println("log info i = " + i);
//              throw new RuntimeException();
//              continue;
            }
        }
    }

结果: 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值