java volatile synchronized关键字使用问题

发布于 2022-09-11 15:02:04 字数 1784 浏览 18 评论 0

题目描述

在学习多线程的知识,下面的代码跑出的结果与预想的不一致,看不明白问题出在哪里,麻烦大家帮忙看看

题目来源及自己的思路

我想的是程序结束后staticv的值是个整数200000,但是每次结果都在19xxxxxxx,离的很近,又不像同步出了问题,不知是哪里出了问题,另外代码注释标注为#的位置,foo被synchronized标志,那同步的是SyncObj的实例对象而不是SyncObjc.class,明明是两个实例对象在被调用没有加同步,我以为volatile没加上去结果是错误的,但执行的结果好像没区别,为什么?我不清楚我的理解哪里出了问题,请大家多多指教,谢谢。

相关代码

package concurrency;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedTest {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        SyncObj obj1 =new SyncObj();
        SyncObj obj2 =new SyncObj();
        CountDownLatch countDownLatch = new CountDownLatch(20);
        for(int i=0;i<10;i++) {
            executor.execute(new TT(obj1,countDownLatch));
            executor.execute(new TT(obj2,countDownLatch));
        }

        countDownLatch.await();
        System.out.println("result----------------------"+SyncObj.staticv);
    }
}

class SyncObj {
    public static volatile int staticv = 0; //######volatile是不是可有可无?
    public  synchronized void foo() {
        for(int i=0;i<1000;i++) {
            System.out.println(staticv);
            staticv++;
        }
    }
}

class TT implements Runnable{
    public CountDownLatch countDownLatch;
    public SyncObj obj;
    public TT( SyncObj obj,CountDownLatch countDownLatch) {
        this.obj=obj;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        for(int i=0;i<100;i++) {
            obj.foo();
        }
        countDownLatch.countDown();
    }
    
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

牛↙奶布丁 2022-09-18 15:02:04

出现问题的原因是 volatile 不提供原子性。
staticv++; 实际上是读取-修改-写入操作序列组成的组合操作。假如线程 A 和线程 B 同时执行 staticv++;,线程 A 读取 staticv 的值,加 1,然后中断,接着线程 B 读取 staticv 的值,加 1,然后写入 staticv。此时线程 A 接着执行,虽然根据 volatile 的特性,线程 A 中 staticv 的值已经变成 +1 之后的值,但是此时线程 A 已经不会重新读取 staticv 了,而是直接把原来读取的 staticv 加 1 之后的值回写到 staticv 上,导致虽然执行了两遍 staticv++;,但是其实 staticv 只增加了 1。

我们再来讨论为什么把 staticv++;synchronized 包起来之后还是没有实现 staticv 自增原子性的效果。
这个问题的关键是你把 synchronized 用到了方法级别。你申请了两个 SyncObj 的对象出来,这两个对象的 foo 方法之间是没有同步关系的,同步关系只在同一个对象的 foo 方法中有。

如果把 foo 方法改成这样就是正确的了。

public void foo() {
    synchronized (this.getClass()) {
        for(int i=0;i<10000000;i++) {
            staticv++;
        }
    }
}
苏别ゝ 2022-09-18 15:02:04

把 SyncObj的 foo 方法改为静态方法, 可以解决你的问题, 因为 new多个 SyncObj 对象后, 对象之间并不能保持同步;


class SyncObj {
    public static volatile int staticv = 0; //######volatile是不是可有可无?
    public static synchronized void foo() {
        for(int i=0;i<1000;i++) {
            //System.out.println(staticv);
            staticv++;
        }
    }
}

或者可以可以共用一个 SyncObj对象

 SyncObj obj1 =new SyncObj();
 CountDownLatch countDownLatch = new CountDownLatch(20);
 for(int i=0;i<10;i++) {
            executor.execute(new TT(obj1,countDownLatch));
            executor.execute(new TT(obj1,countDownLatch));
 }
香橙ぽ 2022-09-18 15:02:04

弄明白了,非常感谢

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文