zoukankan      html  css  js  c++  java
  • synchronized 代码块怎么用

    加不加 synchronized 有什么区别?

    synchronized 作为悲观锁,锁住了什么?

    之前 2 篇文章我们已经知道 synchronized 的使用方法以及锁的内容(实例对象和Class类对象),这已经涵盖了这个关键字的基本内容了,今天这篇想介绍一下另一种写法,就是同步代码块,它实现了更细粒度的同步方式。下面来见分晓。

    先给大家介绍一下同步代码块怎么写,大体的代码框架是这样:

    synchronized(xxx) {
        
    }
    

    xxx 可以是 this 或者 Object 或者 xxx.class,下面我们就根据这 3 种不同加锁方式进行展开讨论。

    this

    表示的是锁住当前对象,和原来使用同步实例方式一样,锁住了当前的对象。

    public class SynchronizedCodeTest {
    
        public static void main(String[] args) {
            SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
            for (int i = 0; i < 5; i ++) {
                Thread thread = new Thread(() -> {
                    synchronizedCodeTest.testSynchronizedCode();
                });
                thread.start();
            }
        }
    
        int count = 0;
        public void testSynchronizedCode() {
            System.out.printf("%s-testSynchronizedCode-start-count=%s
    ", Thread.currentThread().getName(), count);
            synchronized (this) {
                System.out.printf("%s-testSynchronizedCode-synchronized-start-count=%s
    ", Thread.currentThread().getName(), count);
                count ++;
                System.out.printf("%s-testSynchronizedCode-synchronized-end-count=%s
    ", Thread.currentThread().getName(), count);
            }
            System.out.printf("%s-testSynchronizedCode-end-count=%s
    ", Thread.currentThread().getName(), count);
        }
    
    }
    

    运行结果:

    我们主要关注红色框和蓝色框的这部分结果,红色框里面是同步块的代码,线程之间是互斥的,但是蓝色框中Thread-0在执行同步块的过程中,其他线程非同步块也在执行,这里说明了锁的粒度确实变小了,变成了方法里面的同步块代码之间互斥,非同步块代码不互斥,count 的值最终是 5,说明到执行到同步块时,同一时刻只有一个线程在执行。

    我们再写个测试代码,看一看 synchronized(this) 是锁住什么?

    public class SynchronizedCodeTest {
    
        public static void main(String[] args) {
            for (int i = 0; i < 5; i ++) {
                SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
                Thread thread = new Thread(() -> {
                    synchronizedCodeTest.testSynchronizedCode();
                });
                thread.start();
            }
        }
    }
    

    运行结果:

    观察到红色框里面就可以发现,这时候同步块不起效果了,并且 count 最终都是 1,证明 synchronized(this) 锁住的是当前的对象,和 public synchronized void testSynchronizedMethod() 一样。

    Object

    同步代码块带来了灵活性,它不再只是锁住当前对象了,可以锁住任何我们创建的对象,下面就来看看。

    
    public class SynchronizedCodeTest {
    
        public static void main(String[] args) {
            Object lock = new Object();
            SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
            for (int i = 0; i < 5; i ++) {
                Thread thread = new Thread(() -> {
                    synchronizedCodeTest.testSynchroniedLock();
                });
                thread.start();
            }
        }
    
        int count = 0;
    
        Object lock = null;
        public SynchronizedCodeTest(Object lock) {
            this.lock = lock;
        }
    
        public void testSynchroniedLock() {
            System.out.printf("%s-testSynchroniedLock-start-count=%s
    ", Thread.currentThread().getName(), count);
            synchronized (lock) {
                System.out.printf("%s-testSynchroniedLock-synchronized-start-count=%s
    ", Thread.currentThread().getName(), count);
                count ++;
                System.out.printf("%s-testSynchroniedLock-synchronized-end-count=%s
    ", Thread.currentThread().getName(), count);
            }
            System.out.printf("%s-testSynchroniedLock-end-count=%s
    ", Thread.currentThread().getName(), count);
        }
    
    }
    
    

    运行结果:

    这段代码,我们创建了一个 lock 对象,作为参数传入到 synchronizedCodeTest 对象里,我们看到结果里面,5 个线程在同步块代码里是串行执行的,count 最终也得到结果是 5。这段代码没有看出锁对象带来的灵活性,下面再看一个例子,把测试代码稍微改一下,让每个线程都有自己的 synchronizedCodeTest 对象。

    public static void main(String[] args) {
    	Object lock = new Object();
    	for (int i = 0; i < 5; i ++) {
    	SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
    		Thread thread = new Thread(() -> {
    			synchronizedCodeTest.testSynchroniedLock();
    		});
    		thread.start();
    	}
    }
    

    运行结果:

    结果我们发现,虽然我们为每个线程创建一个 synchronizedCodeTest 对象,但是不管怎么运行,5 个线程的同步代码块都是串行执行的,原因在于,我们只创建了一个 lock 对象,这 5 个 synchronizedCodeTest 的 lock 对象都是同一个,因此竞争资源是同一个,才出现这种情况。看是不是比同步方法灵活多了,上一篇中,我们要让多个实例同步执行,我们需要使用静态同步方法,现在不用了,使用同步代码块就可以,只需要满足锁住同一个实例对象就行。

    另外,这个例子的结果是每个实例的 count 最终都为 1,这是因为每个 synchronizedCodeTest 对象都有自己独立的变量 count,所以线程之间互不影响。

    xxx.class

    再来看看最后一种代码块锁 Class 类,这和 public static synchronized testSynchronizedStatic() 的作用是一样的,区别就只是代码块的锁范围可变。我们直接看看代码例子。

    public class SynchronizedCodeTest {
    
        public static void main(String[] args) {
            for (int i = 0; i < 5; i ++) {
                SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
                Thread thread = new Thread(() -> {
                    synchronizedCodeTest.testSynchronizedCodeClass();
                });
                thread.start();
            }
        }
    
        int count = 0;
        public void testSynchronizedCodeClass() {
            System.out.printf("%s-testSynchronizedCodeClass-start-count=%s
    ", Thread.currentThread().getName(), count);
            synchronized (SynchronizedCodeTest.class) {
                System.out.printf("%s-testSynchronizedCodeClass-synchronized-start-count=%s
    ", Thread.currentThread().getName(), count);
                count ++;
                System.out.printf("%s-testSynchronizedCodeClass-synchronized-end-count=%s
    ", Thread.currentThread().getName(), count);
            }
            System.out.printf("%s-testSynchronizedCodeClass-end-count=%s
    ", Thread.currentThread().getName(), count);
        }
    
    }
    

    运行结果:

    每个线程都有自己的实例,但是锁住 Class 会使每个线程实例对象的同步块都是串行执行,这个结果和上面的多个实例锁住同一个 Object 对象的结果是一样的。我个人更倾向于使用锁同一个 Object 对象,而不是锁 Class 类对象。

    总结

    这篇介绍了synchronizd 代码块的 3 种使用方式,并详细介绍了各自的使用方式和区别。简单的列个表。

    类型 使用方式 锁作用范围
    this synchronized(this){} 锁住当前的实例对象
    object synchronized(lock){} 锁住其他实例对象,比较灵活
    xxx.class synchronized(xxx.class){} 锁住 Class 对象

    总共用了 3 篇文章来给大家介绍 synchronized 的具体用法,主要是因为之前有些文章一下子就进入 Java 源代码和 JVM 源码,让不少朋友感觉有点吃力,甚至有朋友一看标题就不进来看看了,所以这次先介绍一些比较简单的用法,让大家先了解了解,后面几篇会比较深入 synchronized 原理性的文章,希望大家也能看看,会有非常大的收获的。

    原创不易,大家多帮忙转发,点在看。

    synchronized 作为悲观锁,锁住了什么?

    加不加 synchronized 有什么区别?

    从 JVM 视角看看 Java 守护线程

    写了那么多年 Java 代码,终于 debug 到 JVM 了

    全网最新最简单的 openjdk13 代码编译

    了解Java线程优先级,更要知道对应操作系统的优先级,不然会踩坑

    线程最最基础的知识

    老板叫你别阻塞了

    吃个快餐都能学到串行、并行、并发

    泡一杯茶,学一学同异步

    进程知多少?

    设计模式看了又忘,忘了又看?

    后台回复『设计模式』可以获取《一故事一设计模式》电子书

    觉得文章有用帮忙转发&点赞,多谢朋友们!

    LieBrother

  • 相关阅读:
    hibernate_0100_HelloWorld
    MYSQL子查询的五种形式
    JSF是什么?它与Struts是什么关系?
    nop指令的作用
    htmlparser实现从网页上抓取数据(收集)
    The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the associated filter. Struts tags are only usable when the
    FCKeditor 在JSP上的完全安装
    Java遍历文件夹的2种方法
    充电电池和充电时间说明
    吃知了有什么好处
  • 原文地址:https://www.cnblogs.com/liebrother/p/12791872.html
Copyright © 2011-2022 走看看