synchronized 关键字详解

Synchronized关键字

synchronized是Java提供的内部锁,里边有类锁和对象锁。synchronized关键字能够用于编辑4种不同类型的块:

  1. 实例方法
  2. 静态方法
  3. 实例方法中的代码块
  4. 静态方法中的代码块

这些块被同步在不同的对象上。使用哪一种synchronized块是取决于实际情况。

 

同步的实例方法

synchronized关键字修饰在实例方法上,则该方法就是同步的实例方法。

/**
 * 同步的实例方法,对象锁
 **/
public synchronized void methodOne() {
    System.out.println(Thread.currentThread().getName() + " methodOne start。。。。");
    try {
        // 假装执行很久
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + " methodOne end。。。。");
}

Java中一个同步的实例方法同步在这个方法所在的实例(锁对象)上。因此,拥有这个方法的实例,在多线程情况下,同一时刻只有一个线程能获取到该方法的执行权,其他线程会阻塞等待,知道上一个线程执行完才开始执行。如果多个实例分别执行自己的线程,则每个实例都能执行,不会阻塞等待。下面通过代码执行结果进行说明。首先是同一个实例,开启多个线程去执行同步的实例方法methodOne(),验证代码(1-1)如下:

 public static void main(String[] args) {
        SynchronizedKeyword synchronizedKeyword = new SynchronizedKeyword();
        new Thread(() -> synchronizedKeyword.methodOne()).start();
        new Thread(() -> synchronizedKeyword.methodOne()).start();
        new Thread(() -> synchronizedKeyword.methodOne()).start();
 }

启动三个线程,使用同一个实例对象synchronizedKeyword去执行同步的实例方法methodOne(),执行结果:

image.png

从结果可以看到,一个线程执行结束后才会执行下一个线程,这个证实同步的实例方法锁的是实例对象,接下来修改执行代码,创建多个实例对象,分别去执行同步的实例方methodOne(),验证代码(1-2)如下:

 public static void main(String[] args) {
        SynchronizedKeyword synchronizedKeyword_1 = new SynchronizedKeyword();
        SynchronizedKeyword synchronizedKeyword_2 = new SynchronizedKeyword();
        SynchronizedKeyword synchronizedKeyword_3 = new SynchronizedKeyword();
        new Thread(() -> synchronizedKeyword_1.methodOne()).start();
        new Thread(() -> synchronizedKeyword_2.methodOne()).start();
        new Thread(() -> synchronizedKeyword_3.methodOne()).start();
 }

启动三个线程,分别使用三不同的实例对象synchronizedKeyword_1synchronizedKeyword_2synchronizedKeyword_3去执行同步的实例方法methodOne(),执行结果:

image.png

从结果可以看到三个线程并没有等待其他线程,几乎是一起开始一起结束。另外从线程执行的顺序还可以看出synchronized 关键字同步的时候,不保证公平性,因此会有线程插队的现象。

 

同步的静态方法

synchronized关键字修饰在静态方法上,则该方法就是同步的静态方法。

/**
 * 同步的静态方法,SynchronizedKeyword.class
 */
public static synchronized void methodTwo() {
    System.out.println(Thread.currentThread().getName() + " methodTwo start。。。。");
    try {
		// 假装执行很久
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + " methodTwo end。。。。");
}

同步的静态方法同步在方法所属的类对象(如SynchronizedKeyword.class)上。由于每个类只有一个类对象存在于JVM中,因此全局同时只有一个线程能够进入到同一个类的静态同步方法中。如果静态同步方法位于不同的类中,则一个线程能够在每个类的静态同步方法中执行。验证代码(2-1)如下:

public static void main(String[] args) {
        SynchronizedKeyword synchronizedKeyword_1 = new SynchronizedKeyword();
        SynchronizedKeyword synchronizedKeyword_2 = new SynchronizedKeyword();
        SynchronizedKeyword synchronizedKeyword_3 = new SynchronizedKeyword();
        new Thread(() -> synchronizedKeyword_1.methodTwo()).start();
        new Thread(() -> synchronizedKeyword_2.methodTwo()).start();
        new Thread(() -> synchronizedKeyword_3.methodTwo()).start();
}

启动三个线程,分别使用三不同的实例对象synchronizedKeyword_1synchronizedKeyword_2synchronizedKeyword_3去执行同步的静态方法methodTwo(),执行结果:

image.png

从结果可以看到,线程是一个接一个顺序执行完的,与验证代码(1-1)的效果一样,由此也可以证实同步的静态方法锁的是类对象。当然如果用同一个实例去执行methodTwo()方法执行结果肯定是一样,在此就不做演示。

 

实例方法的同步代码块

有时不必同步整个方法,因此可以只同步一个方法中的一部分。接下来一起来看看实例方法中的同步代码块是如何使用的。

/**
 * 实例方法的同步代码块,指定锁对象
 */
public void methodThree() {
    synchronized (this) {
        System.out.println(Thread.currentThread().getName() +" methodThree start。。。。");
        try {
            // 假装执行很久
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() +" methodThree end。。。。");
    }
}

同步代码块使得一块代码可以被同步访问。需要注意的是,Java同步代码块与同步方法不一样的是需要指定一个锁对象,可以是实例对象也可以是类对象。在上面的例子中使用的“this”,也就是这个方法调用所在的实例对象。括号中的对象叫做「monitor object」。这段代码被叫做同步在一个monitor object上。因为在例子中我们指定的是实例对象,如果使用同一个实例去执行methodThree()方法同步效果与验证代码(1-1)一样,如果使用多个实例分别去执行methodThree()方法同步效果与验证代码(1-2)一样,在此就不做演示。当然也可以指定锁对象为类对象,如果指定的是类对象,同步效果与验证代码(2-1)一样,感兴趣的朋友可以自行验证。

 

静态方法中的同步代码块

在静态方法中使用同步代码块,则该代码块就是静态方法中的同步代码块。

/**
 * 静态方法中的同步代码块,指定锁对象(只能类对象)
 */
public static void methodFour() {
    synchronized (SynchronizedKeyword.class) {
        System.out.println(Thread.currentThread().getName() +" methodFour start。。。。");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() +" methodFour end。。。。");
    }
}


需要说明一点,由于静态方法是类所属的方法,因此在代码块无法执行实例对象作为锁对象,所以静态方法中的同步代码块只能指定类对象作为锁对象。

 

 

总结

  1. synchronized可以修饰在方法或代码块上
  2. synchronized锁对象包含实例对象和类对象
  3. 静态方法是类所属的方法,因此同步的静态方法和静态方法中的同步代码块都是使用类对象锁

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值