浅析java并发中的Synchronized关键词
如果在多线程的环境中,我们经常会遇到资源竞争的情况,比如多个线程要去同时修改同一个共享变量,这时候,就需要对资源的访问方法进行一定的处理,保证同一时间只有一个线程访问。
java提供了synchronized关键字,方便我们实现上述操作。
为什么要同步
我们举个例子,我们创建一个类,提供了一个setSum的方法:
publicclassSynchronizedMethods{ privateintsum=0; publicvoidcalculate(){ setSum(getSum()+1); } }
如果我们在多线程的环境中调用这个calculate方法:
@Test publicvoidgivenMultiThread_whenNonSyncMethod()throwsInterruptedException{ ExecutorServiceservice=Executors.newFixedThreadPool(3); SynchronizedMethodssummation=newSynchronizedMethods(); IntStream.range(0,1000) .forEach(count->service.submit(summation::calculate)); service.shutdown(); service.awaitTermination(1000,TimeUnit.MILLISECONDS); assertEquals(1000,summation.getSum()); }
按照上面的方法,我们预计要返回1000,但是实际上基本不可能得到1000这个值,因为在多线程环境中,对同一个资源进行同时操作带来的不利影响。
那我们怎么才能够建线程安全的环境呢?
Synchronized关键词
java提供了多种线程安全的方法,本文主要讲解Synchronized关键词,Synchronized关键词可以有很多种形式:
- Instancemethods
- Staticmethods
- Codeblocks
当我们使用synchronized时,java会在相应的对象上加锁,从而在同一个对象等待锁的方法都必须顺序执行,从而保证了线程的安全。
SynchronizedInstanceMethods
Synchronized关键词可以放在实例方法的前面:
publicsynchronizedvoidsynchronisedCalculate(){ setSum(getSum()+1); }
看下调用结果:
@Test publicvoidgivenMultiThread_whenMethodSync(){ ExecutorServiceservice=Executors.newFixedThreadPool(3); SynchronizedMethodsmethod=newSynchronizedMethods(); IntStream.range(0,1000) .forEach(count->service.submit(method::synchronisedCalculate)); service.awaitTermination(1000,TimeUnit.MILLISECONDS); assertEquals(1000,method.getSum()); }
这里synchronized将会锁住该方法的实例对象,多个线程中只有获得该实例对象锁的线程才能够执行。
SynchronizedStaticMethods
Synchronized关键词也可以用在static方法前面:
publicstaticsynchronizedvoidsyncStaticCalculate(){ staticSum=staticSum+1; }
Synchronized放在static方法前面和实例方法前面锁住的对象不同。放在static方法前面锁住的对象是这个Class本身,因为一个Class在JVM中只会存在一个,所以不管有多少该Class的实例,在同一时刻只会有一个线程可以执行该放方法。
@Test publicvoidgivenMultiThread_whenStaticSyncMethod()throwsInterruptedException{ ExecutorServiceservice=Executors.newCachedThreadPool(); IntStream.range(0,1000) .forEach(count-> service.submit(SynchronizedMethods::syncStaticCalculate)); service.shutdown(); service.awaitTermination(100,TimeUnit.MILLISECONDS); assertEquals(1000,SynchronizedMethods.staticSum); }
SynchronizedBlocks
有时候,我们可能不需要Synchronize整个方法,而是同步其中的一部分,这时候,我们可以使用SynchronizedBlocks:
publicvoidperformSynchronizedTask(){ synchronized(this){ setSum(getSum()+1); } }
我们看下怎么测试:
@Test publicvoidgivenMultiThread_whenBlockSync()throwsInterruptedException{ ExecutorServiceservice=Executors.newFixedThreadPool(3); SynchronizedMethodssynchronizedBlocks=newSynchronizedMethods(); IntStream.range(0,1000) .forEach(count-> service.submit(synchronizedBlocks::performSynchronizedTask)); service.shutdown(); service.awaitTermination(100,TimeUnit.MILLISECONDS); assertEquals(1000,synchronizedBlocks.getSum()); }
上面我们同步的是实例,如果在静态方法中,我们也可以同步class:
publicstaticvoidperformStaticSyncTask(){ synchronized(SynchronizedMethods.class){ staticSum=staticSum+1; } }
我们看下怎么测试:
@Test publicvoidgivenMultiThread_whenStaticSyncBlock()throwsInterruptedException{ ExecutorServiceservice=Executors.newCachedThreadPool(); IntStream.range(0,1000) .forEach(count-> service.submit(SynchronizedMethods::performStaticSyncTask)); service.shutdown(); service.awaitTermination(100,TimeUnit.MILLISECONDS); assertEquals(1000,SynchronizedMethods.staticSum); }
本文的例子可以参考https://github.com/ddean2009/learn-java-concurrency/tree/master/Synchronized
ps:synchronized关键字的作用域有二种:
1)是某个对象实例内,synchronizedaMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
总的来说,这种情况,锁就是这个方法所在的对象
2)是某个类的范围,synchronizedstaticaStaticMethod{}防止多个线程同时访问这个类中的synchronizedstatic方法。它可以对类的所有对象实例起作用。此时锁就是这个class
总结
到此这篇关于java并发中的Synchronized关键词的文章就介绍到这了,更多相关javaSynchronized关键词内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。