关于java:synchronized-加锁-this-和-class-的区别

2次阅读

共计 4517 个字符,预计需要花费 12 分钟才能阅读完成。

synchronized 是 Java 语言中解决并发问题的一种罕用伎俩,它也被咱们亲切的称之为“Java 内置锁”,由此可见其位置之高。然而 synchronized 却有着多种用法,当它润饰不同对象时,其意义也是不同的,上面咱们一起来看。

synchronized 用法

synchronized 能够用来润饰 一般办法、静态方法和代码块

① 润饰一般办法

/**
 * synchronized 润饰一般办法
 */
public synchronized void method() {// .......}

当 synchronized 润饰一般办法时,被润饰的办法被称为同步办法,其作用范畴是整个办法,作用的对象是调用这个办法的对象。

② 润饰静态方法

/**
 * synchronized 润饰静态方法
 */
public static synchronized void staticMethod() {// .......}

当 synchronized 润饰动态的办法时,其作用的范畴是整个办法,作用对象是调用这个类的所有对象。

③ 润饰代码块

为了缩小锁的粒度,咱们能够抉择在一个办法中的某个局部应用 synchronized 来润饰(一段代码块),从而实现对一个办法中的局部代码进行加锁,实现代码如下:

public void classMethod() throws InterruptedException {
    // 前置代码...
    
    // 加锁代码
    synchronized (SynchronizedExample.class) {// ......}
    
    // 后置代码...
}

以上代码在执行时,被润饰的代码块称为同步语句块,其作用范畴是大括号“{}”括起来的代码块,作用的对象是调用这个代码块的对象。

但以上代码,除了能够加锁 class 之外,还能够加锁 this,具体示例如下:

public void classMethod() throws InterruptedException {
    // 前置解决代码...
    synchronized (this) {// ......}
    // 后置解决代码...
}

那问题来了,应用 synchronized 加锁 this 和 class 的区别是什么?不都是加锁同一个类吗?

答案还真不是,加锁 this 和 class 区别还是很大的。上面咱们通过以下 4 个示例,来看二者之间的区别。

1. 加锁 class 共享一个类实例

首先,咱们创立 5 个线程,调用同一个对象下 synchronized 加锁的 class 代码,具体示例如下:

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class SynchronizedExample {public static void main(String[] args) {
        // 创立以后类实例
        final SynchronizedExample example = new SynchronizedExample();
        // 创立 5 个线程执行工作
        for (int i = 0; i < 5; i++) {new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 调用 synchronized 润饰的 class 办法
                        example.classMethod();} catch (InterruptedException e) {e.printStackTrace();
                    }
                }
            }).start();}
    }

    /**
     * synchronized 润饰的 class 办法
     * @throws InterruptedException
     */
    public void classMethod() throws InterruptedException {synchronized (SynchronizedExample.class) {
            System.out.println(String.format("以后执行线程:%s, 执行工夫:%s",
                    Thread.currentThread().getName(), new Date()));
            TimeUnit.SECONDS.sleep(1);
        }
    }
}

以上程序的执行后果如下:

从上述后果能够看出,这 5 个线程共享的是同一把锁。

2. 加锁 class 创立多个实例

接下来,咱们创立 5 个线程,调用不同对象下 synchronized 加锁的 class 代码,具体示例如下:

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class SynchronizedExample {public static void main(String[] args) {
        // 创立 5 个线程执行工作
        for (int i = 0; i < 5; i++) {new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 创立类实例
                        SynchronizedExample example = new SynchronizedExample();
                        // 调用 synchronized 润饰的 class 办法
                        example.classMethod();} catch (InterruptedException e) {e.printStackTrace();
                    }
                }
            }).start();}
    }
    
    /**
     * synchronized 润饰的 class 办法
     * @throws InterruptedException
     */
    public void classMethod() throws InterruptedException {synchronized (SynchronizedExample.class) {
            System.out.println(String.format("以后执行线程:%s, 执行工夫:%s",
                    Thread.currentThread().getName(), new Date()));
            TimeUnit.SECONDS.sleep(1);
        }
    }
}

以上程序的执行后果如下:

从上述后果能够看出,尽管是不同的对象,但它们应用的依然是同一把锁。

3. 加锁 this 共享一个类实例

接下来,咱们创立 5 个线程,调用 synchronized 加锁 this 的示例。首先咱们这 5 个线程调用同一个对象的加锁办法,示例代码如下:

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class SynchronizedExample {public static void main(String[] args) {
        // 创立以后类实例
        final SynchronizedExample example = new SynchronizedExample();
        // 创立 5 个线程执行工作
        for (int i = 0; i < 5; i++) {new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 调用 synchronized 润饰的 this 办法
                        example.thisMethod();} catch (InterruptedException e) {e.printStackTrace();
                    }
                }
            }).start();}
    }
    
    /**
     * synchronized 润饰的 this 办法
     * @throws InterruptedException
     */
    public void thisMethod() throws InterruptedException {synchronized (this) {
            System.out.println(String.format("以后执行线程:%s, 执行工夫:%s",
                    Thread.currentThread().getName(), new Date()));
            TimeUnit.SECONDS.sleep(1);
        }
    }
}

以上程序的执行后果如下:

从上述后果能够看出,以上线程应用的都是同一把锁。

4. 加锁 this 创立多个类实例

最初一个示例最为非凡,咱们应用 synchronized 加锁 this,让这 5 个线程调用各自创建对象的办法,具体示例如下:

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class SynchronizedExample {public static void main(String[] args) {
        // 创立 5 个线程执行工作
        for (int i = 0; i < 5; i++) {new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 创立(多个)类实例
                        SynchronizedExample example = new SynchronizedExample();
                        // 调用 synchronized 润饰的 this 办法
                        example.thisMethod();} catch (InterruptedException e) {e.printStackTrace();
                    }
                }
            }).start();}
    }
    
    /**
     * synchronized 润饰的 this 办法
     * @throws InterruptedException
     */
    public void thisMethod() throws InterruptedException {synchronized (this) {
            System.out.println(String.format("以后执行线程:%s, 执行工夫:%s",
                    Thread.currentThread().getName(), new Date()));
            TimeUnit.SECONDS.sleep(1);
        }
    }
}

以上程序的执行后果如下:

从上述后果能够看出,当应用 synchronized 加锁 this 时,如果线程调用的不是同一个对象,那么这些线程之间应用的锁都是本人独立的锁,这个后果就和 synchronized 加锁 class 的后果齐全不同了。

总结

通过以上 4 个示例咱们能够得出结论,当 应用 synchronized 加锁 class 时,无论共享一个对象还是创立多个对象,它们用的都是同一把锁 ,而 应用 synchronized 加锁 this 时,只有同一个对象会应用同一把锁,不同对象之间的锁是不同的

本系列举荐文章

  1. 并发第一课:Thread 详解
  2. Java 中用户线程和守护线程区别这么大?
  3. 深刻了解线程池 ThreadPool
  4. 线程池的 7 种创立形式,强烈推荐你用它 …
  5. 池化技术达到有多牛?看了线程和线程池的比照吓我一跳!
  6. 并发中的线程同步与锁
  7. volatile 和 synchronized 的区别
  8. 轻量级锁肯定比重量级锁快吗?
  9. 这样终止线程,居然会导致服务宕机?
  10. SimpleDateFormat 线程不平安的 5 种解决方案!
  11. ThreadLocal 不好用?那是你没用对!
  12. ThreadLocal 内存溢出代码演示和起因剖析!
  13. Semaphore 自白:限流器用我就对了!
  14. CountDownLatch:别浪,等人齐再团!
  15. CyclicBarrier:人齐了,司机就能够发车了!

关注公号「Java 中文社群」查看更多有意思、涨常识的 Java 并发文章。

正文完
 0