Java中synchronized的四种用法详解

 更新时间:2024年01月23日 08:45:02   作者:码灵  
这篇文章主要介绍了Java中synchronized的四种用法详解,当我们处理多线程处理同步问题的时候就会用到synchronized这个关键字,下面介绍下synchronized的四种用法,需要的朋友可以参考下

问题描述

介绍之前我们先来看下,在java 多线程中 如果没有线程同步会出现什么问题:

下面这个是一个测试例子:

public class MainClass {    public static class MyRun implements Runnable    {        private int count=0;        @Override        public void run() {            while (count<15)            {                    System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");                    count++;                         try {                        Thread.sleep(200);                    }                    catch (Exception e)                    {                    }            }        }    }    public  static void main(String args[])    {          MyRun myRun=new MyRun();        Thread threadA=new Thread(myRun,"A");        Thread threadB=new Thread(myRun,"B");        threadA.start();        threadB.start();    }}

运行结果

ThreadName:A  count:0

ThreadName:B  count:0

ThreadName:A  count:1

ThreadName:A  count:3

ThreadName:B  count:2

ThreadName:A  count:4

ThreadName:A  count:6

ThreadName:A  count:7

ThreadName:B  count:5

我们看到这个count在无序的增加,这个是由于A,B两个线程同时操作Count变量造成的,如果我们想让Count有序增加,应该给

System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
count++;

这段代码同步枷锁,这样当B线程进入这里时候,发现这里已经被锁,就只有等待,A执行完这段代码之后就会释放对这个对象的这段代码的释放锁,A获得了释放锁,就可以进入执行,让A,B有序进入执行,才能让Count有序增加,加入了synchronized之后的代码:

 while (count<15) {
                    synchronized (this)
                    {
                        System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
                    }
                    try {
                        Thread.sleep(200);
                    }
                    catch (Exception e)
                    {
                    }
            }

结果:

ThreadName:A  count:0

ThreadName:B  count:1

ThreadName:B  count:2

ThreadName:A  count:3

ThreadName:A  count:4

ThreadName:B  count:5

ThreadName:B  count:6

ThreadName:A  count:7

ThreadName:B  count:8

加了锁之后A,B线程就可以有序的交替执行,不会同时抢占执行Count++ 操作,

下面介绍synchronised的几种用法

1 锁方法

public synchronized void dodo(){ }

这个就是锁方法,这里面要注意两点:

synchronized 关键子不是方法的一部分,所以它不会被继承,说白了,就是如果父类的方法有synchronized,子类重写这个方法,synchronized不写也不会报错

synchronized 不能修饰接口

synchronized 不能修复构造方法,但是可以修饰构造方法里面的代码块

2 锁代码块

锁代码块就是我上面的那个例子写法了,这个就是锁的是某个对象中的某个代码,让它线程同步。  

 synchronized (this) {
                        System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
 
                    }

我们看到这个synchronized (this) 里面的this,这里是要传入一个对象的,如果不用this也可以,也可以在这个类里面new一个其他对象效果也是一样的,比如

      private Object obj=new Object();
        @Override
        public void run() {
            while (count<15)
            {
                    synchronized (obj)
                    {
                        System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
                    }
                    try {
                        Thread.sleep(200);
                    }
                    catch (Exception e)
                    {
                    }
            }
        }

这里就用了obj这个new的对象,效果和this完全一样

3 锁某个类

public class MainClass {
    public static class MyRun implements Runnable
    {
        public static int count=0;
        @Override
        public void run() {
            while (count<15)
            {
                    synchronized (this)
                    {
                       System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
                    }
                    try {
                        Thread.sleep(200);
                    }
                    catch (Exception e)
                    {
                    }
            }
        }
        public synchronized void dodo()
        {
        }
    }
    public  static void main(String args[])
    {
        MyRun myRun1=new MyRun();
        MyRun myRun2=new MyRun();
        Thread threadA=new Thread(myRun1,"A");
        Thread threadB=new Thread(myRun2,"B");
        threadA.start();
        threadB.start();
    }
}

这个代码里面, 

MyRun myRun1=new MyRun();
 MyRun myRun2=new MyRun();

我new两个MyRun,我前面说过synchronized(this)只能锁某个对象,就是说threadA执行myRun1 threadB执行myRun2,互不干扰,synchronized只能锁自己的run1 或者run2 不能两个对象同时锁到,所以执行的结果是无序的。

ThreadName:A  count:0

ThreadName:B  count:0

ThreadName:B  count:2

ThreadName:A  count:2

ThreadName:B  count:4

ThreadName:A  count:4

ThreadName:A  count:6

ThreadName:B  count:6

ThreadName:A  count:8

ThreadName:B  count:8

如果我们想让两个线程有序执行,这个Count++操作,而且对run1,和run2都同时锁,应该怎么办呢???

答案是锁类,锁类的意思是不管是这个类new了多少对象,这个对象的所有方法,都会上锁,我们更改下代码看看结果,怎么锁类的:

synchronized (MyRun.class) {
                        System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
                    }

我们看到只改动了 synchronized (MyRun.class)这里,其他代码都不变们,这个就把这个MyRun锁了,我们看看结果:

ThreadName:A  count:0

ThreadName:B  count:1

ThreadName:B  count:2

ThreadName:A  count:3

ThreadName:B  count:4

ThreadName:A  count:5

ThreadName:A  count:6

ThreadName:B  count:7

ThreadName:A  count:8

结果是有序的,验证了我们的结果

4 静态方法上的synchronized

public synchronized  static void ddo()

这种方式其实和第三种效果是一样的,都是对类起作用,因为我们知道static修饰的方法是类方法,所以这个synchronized 也是作用于整个类

我们把上面的代码改下看看效果是不是一样:

public class MainClass {
    public static class MyRun implements Runnable
    {
        public static int count=0;
        @Override
        public void run() {
            ddo();
        }
        public synchronized  static void ddo()
        {
            while (count<15)
            {
                    System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                    count++;
                try {
                    Thread.sleep(200);
                }
                catch (Exception e)
                {
                }
            }
        }
    }
    public  static void main(String args[])
    {
        MyRun myRun1=new MyRun();
        MyRun myRun2=new MyRun();
        Thread threadA=new Thread(myRun1,"A");
        Thread threadB=new Thread(myRun2,"B");
        threadA.start();
        threadB.start();
    }
}

结果:

ThreadName:A  count:0

ThreadName:A  count:1

ThreadName:A  count:2

ThreadName:A  count:3

ThreadName:A  count:4

ThreadName:A  count:5

ThreadName:A  count:6

ThreadName:A  count:7

ThreadName:A  count:8

因为A线程 先执行最后满足条件 while (count<15),所以B没有机会执行了,验证符合我们的预期

总结

1、锁如果加在方法上面,或者在方法中的代码块形式,就是锁的这个对象,如果锁是静态方法中,或者代码块synchronized(A.class) 形式 就是锁的这个类,里面的所有方法都会同步。

2、谁拥有了锁,上面线程就拥有了控制这段代码的能力,其他的线程只能看着,只有释放了锁,其他线程才可以操作。

3、synchronized 消耗系统性能,所以能不加锁的逻辑,尽量不要加。

4、操作读写文件,或者数据库,有的时候多线程会出现不可预知的问题,所以要加入锁。

到此这篇关于Java中synchronized的四种用法详解的文章就介绍到这了,更多相关synchronized的用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用idea插件进行java代码生成的操作

    使用idea插件进行java代码生成的操作

    这篇文章主要介绍了使用idea插件进行java代码生成的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • Spring AOP实现接口请求记录到数据库的示例代码

    Spring AOP实现接口请求记录到数据库的示例代码

    这篇文章主要介绍了Spring AOP实现接口请求记录到数据库,代码包括引入AOP依赖及创建日志记录表,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • Spring boot使用spring retry重试机制的方法示例

    Spring boot使用spring retry重试机制的方法示例

    这篇文章主要介绍了Spring boot使用spring retry重试机制的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • Java接口测试Cookie与token原理解析

    Java接口测试Cookie与token原理解析

    这篇文章主要介绍了Java接口测试Cookie与token原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • MyBatis的注解使用、ORM层优化方式(懒加载和缓存)

    MyBatis的注解使用、ORM层优化方式(懒加载和缓存)

    这篇文章主要介绍了MyBatis的注解使用、ORM层优化方式(懒加载和缓存),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • java使用listIterator逆序arraylist示例分享

    java使用listIterator逆序arraylist示例分享

    对于列表而言,除了Iterator,还提供了一个功能更加强大的ListIterator。它可以实现逆序遍历列表中的元素。本示例将使用其逆序遍历ArrayList
    2014-02-02
  • Java的设计模式之代理模式使用详解

    Java的设计模式之代理模式使用详解

    这篇文章主要介绍了Java的设计模式之代理模式使用详解,代理模式是23种设计模式之一,它关心的主要是过程,而不是结果,代理模式主要提供了对目标对象的间接访问方式,即通过代理对象来访问目标对象,需要的朋友可以参考下
    2024-01-01
  • java new一个对象的过程实例解析

    java new一个对象的过程实例解析

    这篇文章主要介绍了java new一个对象的过程实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 浅析Java集合中的LinkedHashSet

    浅析Java集合中的LinkedHashSet

    这篇文章主要介绍了浅析Java集合中的LinkedHashSet,LinkedHashSet 是 Java 中的一个集合类,它是 HashSet 的子类,并实现了 Set 接口,与 HashSet 不同的是,LinkedHashSet 保留了元素插入的顺序,并且具有 HashSet 的快速查找特性,需要的朋友可以参考下
    2023-09-09
  • SpringBoot如何实现starter原理详解

    SpringBoot如何实现starter原理详解

    这篇文章主要介绍了SpringBoot如何实现starter原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06

最新评论