auto.js停止所有线程_Java线程与并发编程实践:深入理解volatile和final变量

同步有两种属性:互斥性和可见性。synchronized关键字与两者都有关系。Java同时也提供了一种更弱的、仅仅包含可见性的同步形式,并且只以volatile关键字关联。

假设你自己设计了一个停止线程的机制(因为无法使用Thread不安全的stop()方法))。清单1中ThreadStopping程序源码展示了该如何完成这项任务。

清单1 尝试停止一个线程

public class ThreadStopping{   public static void main(String[] args)   {      class StoppableThread extends Thread      {         private boolean stopped; // defaults to false         @Override         public void run()         {            while(!stopped)               System.out.println("running");         }         void stopThread()         {            stopped = true;         }      }      StoppableThread thd = new StoppableThread();      thd.start();      try      {         Thread.sleep(1000); // sleep for 1 second      }      catch (InterruptedException ie)      {      }      thd.stopThread();   } }

清单2中的main()方法声明了一个叫做StoppableThread的本地类,它继承自Thread。在初始化完StoppableThread之后,默认的主线程启动和这个 Thread对象关联的线程。之后它睡眠 1 秒,并且在死亡之前调用StoppableThread的stop()方法。

StoppableThread声明了一个被初始化为false的stopped实例变量,stopThread()方法会将该变量设置为true,同时run()方法中的while循环会在每次迭代中检查stopped的值是否已经修改为true。

照下面编译清单2:

javac ThreadStopping.java

运行程序:

java ThreadStopping 你应该能观测到一系列运行时的消息。

当你在单处理器/单核的机器上运行这个程序的时候,很可能会观测到程序停止。但是在一个多处理器的机器或多核单处理器的机器上,可能就看不到程序停止,因为每个处理器或者核心很可能有自己的一份stopped的拷贝,当一条线程修改了自己的拷贝,其他线程的拷贝并没有被改变。

你或许决定使用synchronized关键字以确保只能访问主存中的stopped变量。然后经过一番思考,你决定在清单3中使用同步访问一对临界区的方式来解决这个问题。

清单3 尝试使用synchronized来停止一个线程

public class ThreadStopping{   public static void main(String[] args)   {      class StoppableThread extends Thread      {         private boolean stopped; // defaults to false         @Override         public void run()         {            synchronized(this)            {               while(!stopped)                  System.out.println("running");            }          }         synchronized void stopThread()         {            stopped = true;         }      }      StoppableThread thd = new StoppableThread();      thd.start();      try      {         Thread.sleep(1000); // sleep for 1 second      }      catch (InterruptedException ie)      {      }      thd.stopThread();   }}

出于两个因素考虑,清单3不是一个好主意。尽管你只需解决可见性的问题,synchronized却同时解决了互斥的问题(在该程序中不是个问题)。更重要的是,你还往程序中引进了另一个更严重的问题。

你已经正确地对stopped进行了同步访问,但是进一步观察run()方法中的同步块,尤其是这个while循环。由于正在执行循环的这个线程已经获取了当前StoppableThread对象(通过synchronized(this))的锁,这个循环不会终止。因为默认的主线程需要获取相同的锁,所以它在该对象上调用stopThread()方法的任意尝试都会导致自己被阻塞住。

你可以使用局部变量并在同步块中将stopped的值赋给这个变量来解决这一问题,如下所示:

public void run(){   boolean _stopped = false;   while (!_stopped)   {      synchronized(this)      {         _stopped = stopped;      }      System.out.println("running");   }}

不过,每次循环迭代都要尝试获取锁的方式会存在性能开销(还不如以前),所以这个解决方式是得不偿失的。清单4展示了一个更为高效且整洁的方法。

清单4 尝试通过volatile关键字来停止一个线程

public class ThreadStopping{   public static void main(String[] args)   {      class StoppableThread extends Thread      {         private #####volatile boolean stopped; // defaults to false         @Override         public void run()         {            while(!stopped)               System.out.println("running");         }         void stopThread()         {            stopped = true;         }      }      StoppableThread thd = new StoppableThread();      thd.start();      try      {         Thread.sleep(1000); // sleep for 1 second      }      catch (InterruptedException ie)      {      }      thd.stopThread();   } }

由于stopped已经标记为volatile,每条线程都会访问主存中该变量的拷贝而不会访问缓存中的拷贝。这样,即使在多处理器或者多核的机器上,该程序也会停止。

警告

只有可见性导致问题时,才应该使用volatile。而且,你也只能在属性声明处才能使用这个保留字(如果你尝试将局部变量声明成volatitle,会收到一个错误)。最后,你可以将double和long型的属性声明成volatile,但是应该避免在32位的JVM上这样做,原因是此时访问一个double或者long型的变量值需要进行两步操作,若要安全地访问它们的值,互斥(通过synchronized)是必要的。 当一个属性变量声明成volatile,就不能同时被声明final的。不过,由于Java可以让你安全地访问final的属性而无需同步,这也就不能称之为一个问题了。为了克服DeadlockDemo中的缓存变量问题,我把lock1和lock2都标记成final,尽管也能将它们标记成volatile的。

以后,你会经常使用final关键字来确保在不可变(不会发生改变)类的上下文中线程的安全性。参考清单5。

清单5 借助于final创建一个不可变且线程安全的类

import java.util.Set;import java.util.TreeSet;public final class Planets{   private final Set planets = new TreeSet<>();   public Planets()   {      planets.add("Mercury");      planets.add("Venus");      planets.add("Earth");      planets.add("Mars");      planets.add("Jupiter");      planets.add("Saturn");      planets.add("Uranus");      planets.add("Neptune");   }   public boolean isPlanet(String planetName)   {      return planets.contains(planetName);   }}

清单5展示了一个不可变类Planets,其对象存储着星球名字的集合。尽管集合是可变的,但这个类的设计却保证在构造函数退出之后,集合不会再被改变。通过声明planets为final,这个属性的引用不能被更改。而且,该引用也不能被缓存,所以缓存变量的问题也不复存在。

关于不可变对象,Java提供了一种特殊的线程安全的保证。即便没有用同步来发布(暴露)这些对象的引用,它们依然可以被多条线程安全地访问。不可变对象提供了下列易于识别的规则:

  • 不可变对象绝对不允许状态变更。
  • 所有的属性必须声明成final。
  • 对象必须被恰当地构造出来以防this引用脱离构造函数。

最后一点很让人迷惑,所以这里给出一个this显式地脱离构造函数的简单例子:

public class ThisEscapeDemo{   private static ThisEscapeDemo lastCreatedInstance;   public ThisEscapeDemo()   {      lastCreatedInstance = this;   }}

在www.ibm.com/developerworks/library/j-jtp0618/上查看《Java theory and practice:Safe construction techniques》学习更多常见线程风险的相关知识。


本文节选自《Java线程与并发编程实践》

72ead4aff594088198d3f522a12dbe60.png

Java线程和并发工具是应用开发中的重要部分,备受开发者的重视,也有一定的学习难度。

本书是针对Java 8中的线程特性和并发工具的快速学习和实践指南。全书共8章,分别介绍了Thread类和Runnable接口、同步、等待和通知、线程组、定时器框架、并发工具、同步器、锁框架,以及高级并发工具等方面的主题。每章的末尾都以练习题的方式,帮助读者巩固所学的知识。附录A给出了所有练习题的解答,附录B给出了一个基于Swing线程的教程。

本书适合有一定基础的Java程序员阅读学习,尤其适合想要掌握Java线程和并发工具的读者阅读参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/259113.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目实例改编:利用structs2的action 实时显示图片、pdf和其他内容的框架抽取。(转)...

转自&#xff1a;http://www.verydemo.com/demo_c167_i1382.html 针对&#xff1a;预览文件&#xff08;图片&#xff0c;PDF&#xff09;文件来源为action中的inputStream 重点&#xff1a; structs2的action的配置 action的写法和结果类型 resulttype的写法 网页上实…

开始Go开发之旅-Golang架构师之路系列实战

2019独角兽企业重金招聘Python工程师标准>>> 作者: gomaster.me(冯琪超) 系列:Golang架构师之路 巧妇难做无米之炊&#xff0c;golang sdk就是gopher的大米 下载golang 点击 官网下载golang sdk 根据不同系统&#xff0c;官网下载链接会选择相应的平台进行链接跳转&…

安卓9.0官方系统升级包_华为、荣耀公布可升级安卓10.0机型,你的手机在名单之内吗?...

在近两个月以前&#xff0c;美方将华为关进了小黑屋&#xff0c;随后谷歌也将华为旗下的机型移出了安卓10.0升级名单&#xff0c;这一波操作之后&#xff0c;引起了不小的“恐慌”&#xff0c;许多华为用户也在担心是否还能正常使用安卓系统服务&#xff0c;不过&#xff0c;让…

2. Mysql数据库的入门知识

2. Mysql数据库的入门知识 &#xff08;1&#xff09;打开Windows系统提供的服务查看相应的服务。 &#xff08;2&#xff09;在Windows任务管理器的进程中查看 &#xff08;3&#xff09;使用命令行管理windows的Mysql数据库服务。 Net start 服务名 Net stop 服务名 mysql -h…

nginx php-fpm 输出php错误日志(转)

nginx是一个web服务器&#xff0c;因此nginx的access日志只有对访问页面的记录&#xff0c;不会有php 的 error log信息。 nginx把对php的请求发给php-fpm fastcgi进程来处理&#xff0c;默认的php-fpm只会输出php-fpm的错误信息&#xff0c;在php-fpm的errors log里也看不到ph…

win7优化设置_win7蓝牙怎么打开?

当电脑需要连接蓝牙设备的时候&#xff0c;就需要打开蓝牙设置才行。鉴于一些win7的用户还不知道蓝牙功能在哪&#xff0c;win7蓝牙怎么打开&#xff0c;故系统圣地分享本篇教程。1、win7蓝牙怎么打开?首先要你的电脑支持蓝牙功能。如果你的电脑有蓝牙功能的话那么在电脑的右下…

Doxygen从零学起———安装和配置

Doxygen可以为多种语言生成说明文档&#xff08;从程序的源代码中提取其中按照约定格式写的注释中提取信息&#xff09; 例如C, Objective-C, C#, C, PHP, Python, IDL (Corba, Microsoft, and UNO/OpenOffice flavors), Fortran, VHDL, Tcl, D ,从这期开始&#xff0c;我将系…

Java中的ClassLoader

Java中类的加载过程&#xff08;如Dog类&#xff09;&#xff1a; 通过类型信息定位Dog.class文件。载入Dog.class文件&#xff0c;创建相应的Class对象。执行父类的静态字段定义时初始化语句和父类的静态初始化块。执行子类的静态字段定义时初始化语句和子类的静态初始化块。当…

excel删除无尽空白行_excel如何批量删除空白行 巧用 ctrl+G 只需1秒 最常用的技巧...

工作中我们使用excel通常都会遇到这种情况&#xff0c;就是表格中有很多多余的空行。我们需要把多余的空行删除。 如果空行只有一两行的话&#xff0c;可以把鼠标放在空白行上&#xff0c;然后点击鼠标右键&#xff0c;在弹出的菜单中选择删除菜单。 在弹出的删除确定窗口中&am…

DevOps的前世今生

2019独角兽企业重金招聘Python工程师标准>>> 目前在国外&#xff0c;互联网巨头如Google、Facebook、Amazon、LinkedIn、Netflix、Airbnb&#xff0c;传统软件公司如Adobe、IBM、Microsoft、SAP等&#xff0c;亦或是网络业务非核心企业如苹果、沃尔玛、索尼影视娱乐…

【转】最牛B的编码套路

最近&#xff0c;我大量阅读了Steve Yegge的文章。其中有一篇叫“Practicing Programming”&#xff08;练习编程&#xff09;&#xff0c;写成于2005年&#xff0c;读后令我惊讶不已&#xff1a; 与你所相信的恰恰相反&#xff0c;单纯地每天埋头于工作并不能算是真正意义上的…

ecshop 广告设置

最近公司准备做个商城&#xff0c;让我从JAVA转过去&#xff0c;好吧&#xff0c;先看下吧&#xff0c;反正也得做。接到手里的是一套已经成型的模板&#xff0c;但是二次开发必须得了解下机制、文件、响应、设置什么的&#xff0c;也是个新手&#xff0c;写点东西给后面更新的…

linux 信号_Linux信号机制

信号就是一条消息&#xff0c;通知进程系统中发生了什么事&#xff0c;每种信号都对应着某种系统事件。一般的底层硬件异常是由内核的异常处理程序处理的&#xff0c;它对用户进程来说是透明的。而信号机制&#xff0c;提供了一种方法通知用户进程发生了这些异常。例如&#xf…

DOxygen for C++使用说明——添加数学公式

公式 Doxygen允许你把 公式显示在最终的输出中&#xff08;这个功能仅限于HTML和输出&#xff09;.为了可以在HTML documentation显示公式&#xff08;转化为图片&#xff09;&#xff0c;你必须安装以下软件&#xff1a; latex: 编译器, 被用来解析公式, 首先提取公式写到一…

Tomcat 的 DefaultServlet

问题描述&#xff1a; 群里有人测试 Spring MVC&#xff0c;没有配置任何Controller&#xff0c;只配置了一个view resolver&#xff0c;指定了前缀后缀。 然后&#xff0c;他问的是 当访问 localhost:8080/test 的时候&#xff0c;为什么会被重定向到 localhost:8080/test/ &a…

spss练习数据_SPSS篇——如何在成千上百万个数据中标识重复个案

本文就带大家来学习一个小技巧&#xff0c;如何运用SPSS标识重复个案。我们都知道在Excel中&#xff0c;通常会用到“筛选”功能来选出指定条件相同的单元格。那么在SPSS中&#xff0c;如何在成千上百万个数据中筛选出重复的个案呢&#xff1f; 小编就是要告诉你&#xff0c;几…

DOxygen for C++使用说明——Markdown支持

自Doxygen 版本1.8.0&#xff0c;Markdown被引进。 接下来&#xff0c;我们将先简单介绍标准的Markdown语法&#xff0c;读者可以进入Markdown官网查询更详细的细节。然后讨论一下Doxygen支持的Markdown扩展&#xff0c;最后讨论一下Doxygen对Markdown标准的实现细节。 Stand…

方程式漏洞之复现window2008/win7 远程命令执行漏洞

前几天就想写的&#xff0c;因为一些缘故就没写。此次是在外网环境下进行的。大家在内网中也一个样。 方法&#xff1a; 使用Eternalblue模块&#xff0c;剑测是否有漏洞然后msf生成一个dll直接反弹shell. PS&#xff1a;win版本的不知道缘何生成出来的dll是0kb 我就在自己本地…

客服会话 小程序 如何发起_小程序、公众号、App三者如何融合布局?这里有一份避坑指南...

对产品经理来说&#xff0c;小程序无疑是2020年最火爆的词之一了。我们能看到&#xff0c;就在今年疫情期间&#xff0c;小程序DAU达到4.5亿&#xff0c;而超市、生鲜果蔬、社区购物等都同比增长100个点左右&#xff0c;小程序的商业价值很明显地在快速释放。小程序如此火爆&am…

DOxygen for C++使用说明——注释代码二

这一次我在谷歌搜索中检索到了Doxygen在github的仓库&#xff0c;进去一看&#xff0c;令人大喜&#xff0c;github仓库里含有了一个Doxygen的官方配置文件Doxyfile,于是下载下来&#xff0c;发现Doxyfile已经配置了将仓库中的\src文件编译成Documentation,并且将结果放在了dox…