并发编程之二——synchronized

本文详细探讨了Java中的synchronized关键字,包括其在静态和实例方法、代码块中的应用,对象头的信息分析,以及synchronized的原理。文章还深入讲解了偏向锁的膨胀过程,从无锁到轻量级锁再到重量级锁的升级机制,并介绍了批量重偏向和批量撤销的概念。
摘要由CSDN通过智能技术生成

1、Java中synchronized关键字的使用

synchronized关键字主要用于对java程序对共享资源的访问控制,可以在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized还可以保证一个线程对共享资源的修改被其他线程看到,完全可以替换volatile。

synchronized关键字在Java程序中主要有以下三种应用方式:

(1) 修饰静态方法,相当于对类的class对象加锁。由于静态方法不专属于任何一个实例对象,是类的成员,当线程A访问加锁的静态方法时,线程B就只能等待,具体示例代码如下:

public class SynchronizedForClass {

    static int i = 0;

    /*
     * 此方法是加了synchronized关键字的静态方法
     */
    public static synchronized void synStatic() {
        i++;
    }

    /*
     * 此方法是加了synchronized关键字的普通方法
     */
    public synchronized void synNoStatic() {
        i++;
    }


    public static void main(String[] args) throws InterruptedException {
        // 创建2个线程t1、t2
        Thread t1 = new SynClass(new SynchronizedForClass());
        Thread t2 = new SynClass(new SynchronizedForClass());
        // 启动线程t1、t2
        t1.start();
        t2.start();
        // 主线程等待t1、t2执行完成
        t1.join();
        t2.join();
        // 调用的是静态加锁的方法,因此t1和t2执行完成后,i的值是20000
        System.out.println(i);
    }
}

class SynClass extends Thread {
    private SynchronizedForClass synchronizedForClass;

    public SynClass(SynchronizedForClass synchronizedForClass) {
        this.synchronizedForClass = synchronizedForClass;
    }

    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            synchronizedForClass.synStatic();
        }
    }
}

上述代码执行完结果是i=20000,这便是synchronized对静态方法加锁的作用,加锁的对象是SynchronizedForClass 类对象。线程t1和线程t2对synStatic()方法访问是互斥的,t1在执行synStatic()方法时,t2只能等待t1执行完,因为t2需要竞争SynchronizedForClass 类对象锁,但t2可以在t1执行synStatic()方法时去执行synNoStatic()方法,因为两个方法执行时加锁的对象不同,但是对共享资源i的执行结果就不会再是绝对的i=20000了,而是10000<i<=20000。代码如下:

public class SynchronizedForClass {

    static int i = 0;

    /*
     * 此方法是加了synchronized关键字的静态方法
     */
    public static synchronized void synStatic(){
        i++;
    }

    /*
     * 此方法是加了synchronized关键字的普通方法
     */
    public synchronized void synNoStatic(){
        i++;
    }

 
    public static void main(String[] args) throws InterruptedException {
        // 创建2个线程t1、t2
        Thread t1 = new SynClass(new SynchronizedForClass());
        Thread t2 = new SynClass(new SynchronizedForClass());
        // 启动线程t1、t2
        t1.start();
        t2.start();
        // 主线程等待t1、t2执行完成
        t1.join();
        t2.join();
        // i的值不固定
        System.out.println(i);
    }
}

class SynClass extends Thread {
    private SynchronizedForClass synchronizedForClass;

    public SynClass(SynchronizedForClass synchronizedForClass) {
        this.synchronizedForClass = synchronizedForClass;
    }

    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            synchronizedForClass.synNoStatic();
        }
    }
}

上述代码t1和t2加锁的对象变了,由原来的共享一个SynchronizedForClass 类对象锁,变成了分别拥有各自的SynchronizedForClass 类实例对象锁,因此对共享资源的操作就不再是互斥的,线程安全就无法保证了。

(2) 修饰普通实例方法,相当于对类的实例对象加锁,与类的class对象锁属于不同的锁。

(3) 修饰代码块,对指定对象加锁,进入同步代码块前需要获得指定对象的锁。

Object obj = new Object();
synchronized (obj){
	i++;
}

同步代码块加锁方式可以减小锁的粒度,对于方法体比较大,且方法体内有比较耗时的代码,耗时的代码与共享资源操作无关时,我们可以使用同步代码块单独对共享资源的操作加锁,从而提升代码执行效率。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值