前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java并发中的Synchronized关键词

java并发中的Synchronized关键词

作者头像
程序那些事
发布2020-07-08 15:30:22
2710
发布2020-07-08 15:30:22
举报
文章被收录于专栏:程序那些事程序那些事

java并发中的Synchronized关键词

如果在多线程的环境中,我们经常会遇到资源竞争的情况,比如多个线程要去同时修改同一个共享变量,这时候,就需要对资源的访问方法进行一定的处理,保证同一时间只有一个线程访问。

java提供了synchronized关键字,方便我们实现上述操作。

为什么要同步

我们举个例子,我们创建一个类,提供了一个setSum的方法:

代码语言:javascript
复制

public class SynchronizedMethods {

    private int sum = 0;

    public void calculate() {
        setSum(getSum() + 1);
    }
}

如果我们在多线程的环境中调用这个calculate方法:

代码语言:javascript
复制
    @Test
    public void givenMultiThread_whenNonSyncMethod() throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(3);
        SynchronizedMethods summation = new SynchronizedMethods();

        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关键词可以有很多种形式:

  • Instance methods
  • Static methods
  • Code blocks

当我们使用synchronized时,java会在相应的对象上加锁,从而在同一个对象等待锁的方法都必须顺序执行,从而保证了线程的安全。

Synchronized Instance Methods

Synchronized关键词可以放在实例方法的前面:

代码语言:javascript
复制
    public synchronized void synchronisedCalculate() {
        setSum(getSum() + 1);
    }

看下调用结果:

代码语言:javascript
复制
@Test
public void givenMultiThread_whenMethodSync() {
    ExecutorService service = Executors.newFixedThreadPool(3);
    SynchronizedMethods method = new SynchronizedMethods();
 
    IntStream.range(0, 1000)
      .forEach(count -> service.submit(method::synchronisedCalculate));
    service.awaitTermination(1000, TimeUnit.MILLISECONDS);
 
    assertEquals(1000, method.getSum());
}

这里synchronized将会锁住该方法的实例对象,多个线程中只有获得该实例对象锁的线程才能够执行。

Synchronized Static Methods

Synchronized关键词也可以用在static方法前面:

代码语言:javascript
复制
    public static synchronized void syncStaticCalculate() {
        staticSum = staticSum + 1;
    }

Synchronized放在static方法前面和实例方法前面锁住的对象不同。放在static方法前面锁住的对象是这个Class本身,因为一个Class在JVM中只会存在一个,所以不管有多少该Class的实例,在同一时刻只会有一个线程可以执行该放方法。

代码语言:javascript
复制
    @Test
    public void givenMultiThread_whenStaticSyncMethod() throws InterruptedException {
        ExecutorService service = Executors.newCachedThreadPool();

        IntStream.range(0, 1000)
                .forEach(count ->
                        service.submit(SynchronizedMethods::syncStaticCalculate));
        service.shutdown();
        service.awaitTermination(100, TimeUnit.MILLISECONDS);

        assertEquals(1000, SynchronizedMethods.staticSum);
    }

Synchronized Blocks

有时候,我们可能不需要Synchronize整个方法,而是同步其中的一部分,这时候,我们可以使用Synchronized Blocks:

代码语言:javascript
复制
    public void performSynchronizedTask() {
        synchronized (this) {
            setSum(getSum() + 1);
        }
    }

我们看下怎么测试:

代码语言:javascript
复制
    @Test
    public void givenMultiThread_whenBlockSync() throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(3);
        SynchronizedMethods synchronizedBlocks = new SynchronizedMethods();

        IntStream.range(0, 1000)
                .forEach(count ->
                        service.submit(synchronizedBlocks::performSynchronizedTask));
        service.shutdown();
        service.awaitTermination(100, TimeUnit.MILLISECONDS);

        assertEquals(1000, synchronizedBlocks.getSum());
    }

上面我们同步的是实例,如果在静态方法中,我们也可以同步class:

代码语言:javascript
复制
    public static void performStaticSyncTask(){
        synchronized (SynchronizedMethods.class) {
            staticSum = staticSum + 1;
        }
    }

我们看下怎么测试:

代码语言:javascript
复制
    @Test
    public void givenMultiThread_whenStaticSyncBlock() throws InterruptedException {
        ExecutorService service = 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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要同步
  • Synchronized关键词
    • Synchronized Instance Methods
      • Synchronized Static Methods
        • Synchronized Blocks
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档