• 佑儿
    2019-05-20
    stop和start方法对于terminated访问由于syn关键字,线程安全,但是start中新起了一个线程rptthread,导致stop方法中对于terminated存在可见性问题,因此需要volatie,原子性问题对这个代码段没有影响,所以原子性问题无需关注。

    作者回复: 👍

    
     19
  • 孙志强
    2019-05-18
    有必要,变量被多个线程访问,需要保证可见性

    作者回复: 👍

     1
     13
  • echo_陈
    2019-05-18
    我觉得,在本例子中。stop中,设置终止标识位对interupt是可见的。而interrupt对被中断线程检测到中断事件是可见的……根据传递性原则……我觉得本例子不需要volatile关键字。但平时开发中,一般会加上,主要是因为怕后续开发不注意这些事情导致修改破坏了规则,引起可见性问题产生bug,保险起见会加上volatile

    作者回复: 是的,线程不调用wait,sleep等方法,是无法响应中断的,这个时候基于interrupt的可见性就不成立了,所以工程上这类变量都需要加volatile

     3
     10
  • WL
    2019-05-20
    请问一下老师"JVM 的异常处理会清除线程的中断状态"指的是什么意思, 是指把线程的为true的中断状态改为false吗, JVM是在catch到Interrupt异常的时候重置线程中断状态的吗?

    作者回复: 是的

    
     6
  • ZOU志伟
    2019-05-18
    shutdown()调用后,还要再调用awaitTermination方法等待一点时间,线程池里的线程才会终止。
    
     5
  • 晓杰
    2019-05-19
    有必要,因为stop方法对isTerminated的修改需要被start方法读取到,保证共享变量的可见性
    
     4
  • 遇见阳光
    2019-05-18
    按道理而言,synchronized保证原子性的同时,也能间接的保证可见性啊。感觉可以不加 volatile关键字

    作者回复: 问题是start方法里又启动了一个新的线程,synchronized管不到这个新的线程

    
     4
  • null
    2019-06-08
    ```java
    // 因为留言超字数:1. 省略未修改的代码片段,2. println 是 System.out.println 的简写
    class Proxy {
    // 变量声明,(留言超字数,此处未做修改,省略)

    public static void main(String[] args) {
      Proxy proxy=new Proxy();
      for (int i=0; i<100; i++) {
        new Thread(() -> {
        proxy.start();
        proxy.stop();
        }, "外部线程_"+i)
        .start();
      }
    }

    // 启动采集功能
    synchronized void start() {
      // 不允许同时启动多个采集线程
      String outerName=Thread.currentThread().getName();
      println("["+outerName+"]线程是否启动?"+started);

      if (started) {
        println("["+outerName+"]线程 return");
        return;
      }
      started=true;
      terminated=false;

      rptThread=new Thread(() -> {
        while (!terminated) {
          // 每隔两秒钟采集、回传一次数据(留言超字数,此处未做修改,省略)
        }
        // 执行到此处说明线程马上终止
        started=false;
        println("["+outerName+",内部线程:"+Thread.currentThread().getName()+"] started=false 成功执行");
      });

      rptThread.start();
      println("["+outerName+"]线程执行完毕,内部子线程正在执行中...");
    }

    // 终止采集功能(留言超字数,此处未做修改,省略)
    }
    ```

    ```
    执行结果:
    [外部线程_77]线程是否启动?false
    [外部线程_77]线程执行完毕,内部子线程正在执行中...
    [外部线程_82]线程是否启动?true
    [外部线程_82]线程 return
    [外部线程_81]线程是否启动?false
    [外部线程_77,内部线程:Thread-72] started=false 成功执行
    [外部线程_81]线程执行完毕,内部子线程正在执行中...
    [外部线程_81,内部线程:Thread-73] started=false 成功执行
    [外部线程_84]线程是否启动?false
    [外部线程_84]线程执行完毕,内部子线程正在执行中...
    [外部线程_80]线程是否启动?true
    [外部线程_84,内部线程:Thread-74] started=false 成功执行
    [外部线程_80]线程执行完毕,内部子线程正在执行中...
    [外部线程_79]线程是否启动?true
    [外部线程_80,内部线程:Thread-75] started=false 成功执行
    ```

    解释说明:
    1. “[外部线程_81]线程是否启动?false” 先于 “[外部线程_77,内部线程:Thread-72] started=false 成功执行”:
    [外部线程_77,内部线程:Thread-72] 执行完 started=false,还没执行 System.out 输出语句,[外部线程_81] 就已经拿到 started=false 的结果了。

    2. “[外部线程_80]线程是否启动?true” 然后又 “[外部线程_80]线程执行完毕,内部子线程正在执行中...”:
    这时[外部线程_80]让出了 cpu,等到时间片后再次执行时并没有 return,而是成功执行了内部子线程。

    结论:started 在线程之间可以保证可见性的,但是具体原因,自己也没想明白。

    -----

    自己套用了下面的 Happens-Before 规则:
    0. Happens-Before 的传递性。
    1. 管程中锁的规则。
    2. 线程启动规则。
    3. 线程终止规则。
    4. 线程中断规则。
    好像也无法推导出:为何在内部线程 rptThread 修改的 started 变量,可以保证可见性。
    是根据什么规则,保证了 started 变量的可见性,老师可以帮忙分析一下么?期待您的回复,谢谢老师!!
    展开

    作者回复: println内部有锁,而且还有io操作,所以会让结果不准确

     2
     2
  • jason
    2020-01-11
    按这样说,新线程里会修改start变量的值,为了保证start的最新值能被start()方法看见,是不是也要对start变量加volatile修饰?

    作者回复: 加上更保险,不加数据也不会错,只是可能需要多等一会

    
     1
  • 虚竹
    2019-10-12
    王老师好,请教下:
    1.自定义标志位终止线程时,是不是这样可以这样写?
    whlie(!Thread.currentThread()isInterrupted() || !terminated){}
    2.线程池关闭时,完整的是这样吧?
    exec.shutdown();
    while(true){
      if(exec.isTerminated()){
        print("所有的任务都结束了~");
      }
      Thread.sleep(1000);
    }

    展开

    作者回复: 可以这样写

    
     1
  • 远东通信-应用软件
    2019-10-12
    在本章节后面一个实例代码中while循环没有使用线程的中断标志位做判断,但是stop里面仍然去调用rptThread.interrupt()有必要吗?只是为了将采集线程从sleep状态唤醒吗?

    作者回复: 是的

    
     1
  • 其
    2019-07-16
    老师,想问一个问题如果interrupt()方法只是给线程打一个中断的标签,那么如果我线程本身没有显示的去做这个标的判断,线程还能被中断么,当然线程是runnable的,如果能中断又是谁去识别的呢?

    作者回复: 线程不能被中断,但是很多系统函数如sleep是响应中断的。极端地讲,纯CPU计算一定不会被中断

    
     1
  • 胡小禾
    2019-07-08
    第一段代码中的第九行:
          started = true;
    有必要存在吗?

    作者回复: 核心逻辑是异步执行的,所以还是有必要的

    
     1
  • 八百
    2019-05-26
    我记得在kafka0.8版本的时候,我设置中断来关闭消费者线程,结果不行,然后我把中断标志位,恢复了,就好了。。Ծ‸Ծ
    
     1
  • ban
    2019-05-24
    老师,思考题前的最后一个示例代码,为什么
    // 线程终止标志位
    volatile boolean terminated = false;
    boolean started = false;

    为什么started可以不加volatile,terminated却要加呢?
    展开

    作者回复: started的读写都在同步方法里面

    
     1
  • shangyu
    2019-05-23
    请问老师 用interrupt方式中断线程是设置标志位,在run方法中用循环检测该中断标志位,如果中断则不再循环。
    这里有个问题是必须执行完循环里的代码,重新检测循环条件才能中断,那有没有办法能在循环中中断,实现类似ctrl-C的功能,用Thread.stop吗?
    
     1
  • 佑儿
    2019-05-20
    两阶段终止模式:发送终止指令+响应终止指令。
    终止指令通常可以定义一个终止标识变量(注意并发问题,需要volatie保证可见性)。
    如果线程中调用了可中断方法(wait等),在发送终止指令的同时需要调用Thread.interrupt()。
    不建议使用线程自身的中断标识作为终止指令,因为项目中第三方的调用无法保证该标志位。
    
     1
  • IF You
    2020-01-16
    本文中的start()方法有问题,使用的sleep()方法,而该方法不会释放锁,导致调用stop的方法阻塞在获取监视器锁的状态,永远不能通过stop方法优雅停止线程
    
    
  • potato00fa
    2019-11-02
    有必要加volatie,父线程调用stop改标志位通知子线程,标志位属于跨线程的共享变量
    
    
  • 十大杰出青年
    2019-10-28
    老师,可以举一个调用第三方类库,第三方类库捕获了异常,却没有正常处理的例子吗?
    
    
我们在线,来聊聊吧