synchronized关键字使用详解

栏目: Java · 发布时间: 5年前

内容简介:计算机单线程在执行任务时,是严格按照程序的代码逻辑,按照顺序执行的。因此单位时间内能执行的任务数量有限。为了能在相同的时间内能执行更多的任务,就必须采用多线程的方式来执行(作用代码块时,synchronized方法中的this,是指调用该方法的对象。需要主要的是,synchronized作用代码块时,只会锁住这一小块代码。代码块的上下部分的其他代码在所有的线程仍然是能同时访问的。同时需要注意的是每个对象有用不同的锁。即不会阻塞不同对象的调用。synchronized作用在方法上,其实是缺省了this关键字,

简述

计算机单线程在执行任务时,是严格按照程序的代码逻辑,按照顺序执行的。因此单位时间内能执行的任务数量有限。为了能在相同的时间内能执行更多的任务,就必须采用多线程的方式来执行( 注意:多线程模式无法减少单次任务的执行时间 )。但是引入了多线程之后,又带来了线程安全的问题。而为了解决线程安全的问题,又引入了锁的概念。java中常用的锁有 synchronizedlock 两种,本文我们来分析 synchronized 的具体用法和使用注意事项。

基本使用

同步代码块

/**
 * 同步代码块
 * @throws Exception
 */
public void synchronizedCode() {
    try {
        synchronized (this) {
            System.out.println(getCurrentTime() + ":I am synchronized Code");
            Thread.sleep(5000);//延时5秒,方便后面测试
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

作用代码块时,synchronized方法中的this,是指调用该方法的对象。需要主要的是,synchronized作用代码块时,只会锁住这一小块代码。代码块的上下部分的其他代码在所有的线程仍然是能同时访问的。同时需要注意的是每个对象有用不同的锁。即不会阻塞不同对象的调用。

同步方法

/**
  * 同步方法
  */
public synchronized void synchronizedMethod() {
    try {
        System.out.println(getCurrentTime() + ":I am synchronized method");
        Thread.sleep(5000);//延时5秒,方便后面测试
    } catch (Exception e) {
        e.printStackTrace();
    }
}

synchronized作用在方法上,其实是缺省了this关键字,实际上是synchronized(this)。this是指调用该方法的对象。此锁也不会阻塞不同对象之间的调用。

同步静态方法

/**
* 同步静态方法
*/
public synchronized static void synchronizedStaticMethod() {
    try {
        System.out.println(getCurrentTime() + ":I am synchronized static method");
        Thread.sleep(5000);//延时5秒,方便后面测试
    } catch (Exception e) {
        e.printStackTrace();
    }
}

使用方式和作用普通方式相同,唯一需要注意的地方是此锁所有对象共用,即不同对象之间会阻塞调用。

测试准备

简单说明一下:有一个线程池,在执行多任务时使用。每个同步方法或者代码块中都有一个休眠5秒的动作,利用打印时间加休眠来看线程之间是否有阻塞效果。然后有一个1秒打印一次时间的方法。

public class Synchronized {
    //打印时间时格式化
    public static final String timeFormat = "HH:mm:ss";
    //执行多任务的线程池
    public static final ExecutorService executor = Executors.newFixedThreadPool(4);

    /**
     * 同步代码块
     * @throws Exception
     */
    public void synchronizedCode() {
        try {
            synchronized (this) {
                System.out.println(getCurrentTime() + ":I am synchronized Code");
                Thread.sleep(5000);//延时5秒,方便后面测试
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 同步方法
     */
    public synchronized void synchronizedMethod() {
        try {
            System.out.println(getCurrentTime() + ":I am synchronized method");
            Thread.sleep(5000);//延时5秒,方便后面测试
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 同步静态方法
     */
    public synchronized static void synchronizedStaticMethod() {
        try {
            System.out.println(getCurrentTime() + ":I am synchronized static method");
            Thread.sleep(5000);//延时5秒,方便后面测试
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 循环打印时间
     */
    public static void printNumber() {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        printOnceASecond();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    /**
     * 一秒打印一次时间
     *
     * @throws Exception
     */
    public static void printOnceASecond() throws Exception {
        System.out.println(getCurrentTime());
        Thread.sleep(1000);
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    public static String getCurrentTime() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(timeFormat));
    }
}

OK,接下来我们就来测试下锁的互斥性以及使用注意事项(都是 多线程 的情况下)。

开始测试

同一个对象同步代码块

public static void main(String[] args) throws Exception {
    printNumber();//控制台循环打印时间
    Synchronized es = new Synchronized();
    executor.execute(() -> es.synchronizedCode());
    executor.execute(() -> es.synchronizedCode());
}

execute

20:34:41:I am synchronized Code
20:34:41
20:34:42
20:34:43
20:34:44
20:34:45
20:34:46:I am synchronized Code

同步代码块中休眠5秒,导致另外一个线程阻塞5秒后再执行。说明代同步码块会阻塞同一个对象的不同线程之间的调用(同步方法和同步静态方法也会阻塞同一个对象的不同线程之间的调用,此处省略测试代码)

不同对象同步代码块

public static void main(String[] args) throws Exception {
    printNumber();//控制台循环打印时间
    Synchronized es = new Synchronized();
    Synchronized es1 = new Synchronized();
    executor.execute(() -> es.synchronizedCode());
    executor.execute(() -> es1.synchronizedCode());
}

execute

20:44:34:I am synchronized Code
20:44:34:I am synchronized Code

由结果可以看出,不同对象之间代码块锁互不影响(多线程也一样)。原因是因为代码块中 synchronized (this)

锁的是当前调用对象,不同对象之间不是同一把锁,因此互不影响(同步方法原理也是如此,省略测试代码)。

同一对象同步代码块和方法

public static void main(String[] args) throws Exception {
    printNumber();//控制台循环打印时间
    Synchronized es = new Synchronized();
    executor.execute(() -> es.synchronizedCode());
    executor.execute(() -> es.synchronizedMethod());
}

execute

20:51:27:I am synchronized method
20:51:27
20:51:28
20:51:29
20:51:30
20:51:31
20:51:32:I am synchronized Code

因为同步代码块和同步方法,都是锁当前调用对象,因此执行后打印上述结果应该在意料之中。基于这样的特性,实际开发在使用spring的时候就需要注意了,我们的bean交给spring容器管理之后,默认都是单例的。那么这个时候使用 synchronized 关键字就需要注意了(推荐使用同步代码块,同步的代码块中传入外部定义的一个变量)。

不同对象静态同步方法

public static void main(String[] args) throws Exception {
    printNumber();//控制台循环打印时间
    Synchronized es = new Synchronized();
    Synchronized es1 = new Synchronized();
    executor.execute(() -> es.synchronizedStaticMethod());
    executor.execute(() -> es1.synchronizedStaticMethod());
}

execute

21:05:39:I am synchronized static method
21:05:40
21:05:41
21:05:42
21:05:43
21:05:44:I am synchronized static method

由上述结果可以看出来,静态同步方法会阻塞所有的对象。原因是所有的静态同步方法都是占用的同一把锁。

相同对象同步方法和静态同步方法

public static void main(String[] args) throws Exception {
    printNumber();//控制台循环打印时间
    Synchronized es = new Synchronized();
    executor.execute(() -> es.synchronizedMethod());
    executor.execute(() -> es.synchronizedStaticMethod());
}

execute

21:11:03:I am synchronized static method
21:11:03:I am synchronized method

由此结果可以看出,同步方法和静态同步方法之间不会造成阻塞的现象。因为他们锁的对象不一样。同步方法占用的锁是调用对象,静态同步方法锁的是编译后的class对象。

总结

不同对象之间,同步方法、同步代码块不会互斥。静态同步方法会互斥


以上所述就是小编给大家介绍的《synchronized关键字使用详解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

编程卓越之道

编程卓越之道

海德 / 张菲 / 电子工业出版社 / 2007-4 / 69.00元

《编程卓越之道第二卷:运用底层语言思想编写高级语言代码》是《编程卓越之道》系列书的第二卷,将探讨怎样用高级语言(而非汇编语言)编程得到高效率机器代码。在书中,您可以学到如何分析编译器的输出,以便检验代码的所作所为,从而得到高质量的机器码;了解编译器为常见控制结构生成的典型机器指令,以便在编写高级语言程序时选用恰当的语句;掌握编译器将各种常量和变量类型转换成机器数据的方法,以便于使用这些数据写出又快......一起来看看 《编程卓越之道》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具