Java中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的用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Spring boot使用spring retry重试机制的方法示例
这篇文章主要介绍了Spring boot使用spring retry重试机制的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2020-01-01java使用listIterator逆序arraylist示例分享
对于列表而言,除了Iterator,还提供了一个功能更加强大的ListIterator。它可以实现逆序遍历列表中的元素。本示例将使用其逆序遍历ArrayList2014-02-02
最新评论