volatile疑问记录

对java中volatile关键字的描述,主要是可见性和有序性两方面。

一个很广泛的应用就是使得多个线程对共享资源的改动变得互相可见,如下:

public class TestVolatile extends Thread {
    /*A*/
//    public volatile boolean runFlag = true;
    public boolean runFlag = true;

    public boolean isRunFlag() {
        return runFlag;
    }

    public void setRunFlag(boolean runFlag) {
        this.runFlag = runFlag;
    }

    @Override
    public void run() {
        System.out.println("进入run");
        while (isRunFlag()) {
            /*B*/
//            System.out.println("running");
        }
        System.out.println("退出run");
    }

    public static void main(String[] args) throws InterruptedException {
        TestVolatile testVolatile = new TestVolatile();
        testVolatile.start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        testVolatile.setRunFlag(false);
        System.out.println("main already set runflag to false");
        new CountDownLatch(1).await();
    }
}

在A处如果不将运行标记(runflag)设置成volatile,那么main线程对runflag的修改对于testVolatile线程将不可见。导致其一直不打印“退出run”这句。

但是如果在testVolatile线程的while()增加一句:B处打印语句,程序却达到了不使用volatile,修改也变得可见,不知道到底是什么原理。

只能大概估计是while()的执行过程中线程上下文进行了切换,使得重新去主存获取了runflag的最新值,从而退出了循环,暂时记录…

2017/3/8日更新
和群里面的朋友讨论了一下,发现同一份代码,不同的机器运行出了不一样的效果。又仔细翻阅了一下《effective java》,依稀记得当时好像遇到过这个问题,果然,在并发的第一张就对这个现象做出了解释。
关键就在于HotSpot Server VM对编译进行了优化,这种优化称之为提升(hoisting),结果导致了活性失败(liveness failure)

while (isRunFlag()) {}

会被优化成

if(isRunFlag()){
    while(true)...
}

引用effective java这一节的原话:

简而言之,当多个线程共享可变数据的时候,每个读或者写数据的线程都必须执行同步
如果没有同步,就无法保证一个线程所做的修改可以被另一个线程获知。未能同步共享可变数据会造成程序的活性失败和安全性失败。这样的失败是难以调式的。他们可能是间歇性的,且与时间相关,程序的行为在不同的VM上可能根本不同,如果只需要线程之间的交互通信,而不需要互斥,volatile修饰符就是一种可以接受的同步形式,但是正确的使用它可能需要一些技巧。

<think>嗯,用户问的是“this.startTime = System.currentTimeMillis();”这段代码。首先,我需要确定用户对Java的熟悉程度。可能他们刚开始学,或者在使用某个需要计时的功能时遇到了这段代码。 先看代码结构。左边是实例变量startTime,用this引用的,说明是在当前类的实例中。右边调用了System类的currentTimeMillis()方法。这个方法的作用是返回当前时间戳,单位是毫秒。所以这行代码是在记录某个开始时间。 接下来要考虑用户可能的疑问:为什么要用System.currentTimeMillis()?和System.nanoTime()有什么区别?这时候需要解释两者的适用场景。currentTimeMillis适合计算日期时间或较长的间隔,而nanoTime更精确,适合测量短时间操作,比如性能测试。 然后,用户可能想知道这行代码的应用场景。常见的例子是性能监控,比如方法执行时间,或者在游戏、定时任务中记录开始时间,以便后续计算经过的时间。 还要注意潜在的问题。currentTimeMillis返回的是系统时间,如果系统时间被修改(比如用户调整了时钟),会导致计算不准确。这时候可能需要用nanoTime来避免这个问题。同时,处理时间戳溢出虽然可能性低,但也要提一下。 需要确保用户明白这行代码的作用:获取当前时间并存储,以便后续计算时间差。可能的话,举一个简单的例子,比如计算代码执行时间,这样更直观。 另外,检查是否有语法错误或常见错误。比如,是否忘记初始化startTime变量,或者作用域的问题。但用户的问题可能只是理解这行代码,不需要深入错误排查,除非他们后续提问。 最后,总结这行代码的用途和注意事项,确保回答结构清晰,分点说明,帮助用户逐步理解。</think>好的,我来逐步解释这行代码的作用和原理: 1. **代码功能** `this.startTime = System.currentTimeMillis();` 是Java中用于记录当前时间戳的常见写法: - `System.currentTimeMillis()` 是Java标准库方法,返回自 **1970年1月1日00:00:00 UTC(纪元时间)** 到当前时间的毫秒数 - `this.startTime` 表示将获取的时间戳赋值给当前对象的实例变量 `startTime` 2. **典型使用场景** ```java // 记录方法开始执行时间 public void processData() { long startTime = System.currentTimeMillis(); // ...执行耗时操作... long duration = System.currentTimeMillis() - startTime; System.out.println("耗时:" + duration + "ms"); } ``` 3. **与nanoTime()的区别** | 方法 | 精度 | 用途 | 是否受系统时间影响 | |------------------------|------------|---------------------------|------------------| | `currentTimeMillis()` | 毫秒级 | 记录绝对时间/计算较长间隔 | 是(依赖系统时钟)| | `nanoTime()` | 纳秒级 | 测量短时间操作性能 | 否 | 4. **注意事项** - 时间戳溢出问题:`long`类型最大值为9223372036854775807,可表示约2.9亿年 - 系统时间修改风险:如果运行过程中系统时间被人工调整,会导致计算结果错误 - 多线程环境下建议对共享变量`startTime`使用`volatile`关键字 5. **扩展应用** ```java // 计算代码块执行时间(单位:秒) long start = System.currentTimeMillis(); // ...执行代码... double elapsedSeconds = (System.currentTimeMillis() - start) / 1000.0; ``` 这行代码本质上是计算机系统中 **时间戳获取** 的基础操作,在性能分析、定时任务、游戏开发等领域有广泛应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值