在前面的学习同步锁显示了两个属性:互斥和可见性。同步关键字连接着这两个属性。Java提供一个弱引用的仅仅用于同步可见性。它也连接着一个volatile的关键字。

假设你设计一个机制去停止一个线程(因为你不能用Thread的stop()方法,在这个任务中它是不安全的)。Listing2-2显现出了代码资源并于ThreadStoping的应用,和展示了你可能完成的任务。

Listing2-2

package com.owen.thread.chapter2;public class SynThreadStopping
{public static void main(String[] args){class StoppableThread extends Thread{private boolean stopped; // defaults to false@Overridepublic 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();}
}

Listing2-2main()方法声明了一个局部的类,全名为StoppableThread,它是Thread的子类。之后实例化StoppableThread,这个默认主线程开启一个线程连接着Thread对象。在线程死亡之前,它会先休眠一秒钟,然后调用StoppableThread的stop()方法。

StoppableThread在实例域声明一个stopped的变量,并且初始时的值是false,一个stopThread()方法设置stopped的值是true,一个run()方法在每一次循环时都会去检查stopped的值是否改变为true。

运行上在的程序,你需要观察运行信息的顺序。

当你的程序运行在单处理器或单核的机器上,你将可能观察到应用停止。

在一个多处理器机器或多核的唯一处理器上,你可能不会看到这个停止,因为每一个处理器或核可能有它自己的副本stopped在缓存中。当一个线程修改它自己域的副本,其它线程拷贝的stopped将为不会改变。

你可能决定使用同步关键字去确保这个拷贝的stopped,在仅仅一个主要的内存调用中。之后你给在一对临界区的资源代码中结束这个同步,这个可能出现在下面的例子Listing2-3

Listing2-3通过同步关键字去停止一个线程

package com.owen.thread.chapter2;public class SynThreadStopping2_3
{public static void main(String[] args){class StoppableThread extends Thread{private boolean stopped; // defaults to false@Overridepublic 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();}
}

Listing2-3是一个不好的思想,表现在两点:第一,尽管你仅仅需要去解决可见性问题,同步也解决了互斥问题(在这个例子中没有看到)。更重要的,在这个例子中你已经面临着一系列问题。

你要正确通过stopped来同步,但是你看同步阻塞在run()方法。注意while的循环。这个循环是不会结束的,因为线程执行循环体时,需要请求锁给当前的StoppableThread对象(通过synchronized(this)),和任何企图通过主线程去请求stopThread()中的对象,都会导致主线程的阻塞,因为主线程也需要同样的锁。

你可以通过局部变量来解决这个问题,在同步阻塞中注册stopped的值给这个变量,如下代码:

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

然而,这个问题是混乱的和浪费资源的,因为当企图去请求锁时,应用需要花销,和每一次循环时都要去完成这个任务。Listing2-4的例子会更高效和更清晰。

package com.owen.thread.chapter2;public class SynThreadStopping2_4
{public static void main(String[] args){class StoppableThread extends Thread{private volatile boolean stopped; // defaults to false@Overridepublic 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,每个线程都会通过主内存拷贝这个变量的副本,而不是通过缓存副本。这个应用将会停止,尽管在多处理器机器或多核机器。

Caution  使用volatile仅仅在这里可见性上发挥作用。你也可以声明这个字节在上下文领域(如果你试图让一个局部变量中加入volatile,那么你将会收到错误信息。)最后,你可以声明double和long域的volatitle,避免在32位的JVM,因为它需要两个操作访问double或long的变量值,和互斥(通过同步)需要访问他们的安全价值。

当全局的变量声明为volatile,它就不能再声明为final。然而,这并不是个问题,因为Java也可以让你安全通过final的局部却不需要同步。为了去解决缓存的问题,在DeadlockDemo的例子,我标记了两个lock1和lock2为final,当然我也可以标记为volatile。

你将会经常使用final去帮助你在一个不变的上下文类中确保线程安全。思考Listing2-5.

Listing2-5创建一个不变的和线程安全的类。

package com.owen.thread.chapter2;import java.util.Set;
import java.util.TreeSet;public final class Planets
{private final Set<String> 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);}
}

Listing2-5显现一个不变的Planets的类,它的对象存储一个Set的集合中。尽管Set集合是可以改变的,这个类的设计防止在构造函数退出后修改集合。通过声明planets为final,这个引用存储在这个局部并且不能改变。然而,这个引用不能被缓存,所以这个缓存变量问题不存在。

Java提供一个特殊的安全线程确保不变量对象。这个对象可以在多线程中安全被调用,即使不使用同步来显示它们的引用,只要遵守以下规则:

不变对象不允许状态改变。

所有的全局都要声明为final.

对象必须正常被构造,这样“this”引用也不会从构造器逃逸。

最后一点问题可能会有点混淆,下面的例子说明了“this”引用从构造器中逃逸现象。

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

源码下载:git@github.com:owenwilliam/Thread.git

2.4变动和最终变量(Volatile and Final Variables)相关推荐

  1. java中的最终变量_在lambda表达式中使用的变量应该是最终变量或有效的最终变量。...

    A final变量意味着它只能被实例化一次.在Java中,您不能在lambda和匿名内部类中使用非最终变量. 您可以使用旧的for-each循环重构代码:private TimeZone extrac ...

  2. java为什么复制数组会减一_如果从数组中复制了Java,为什么Java需要对最终变量进行显式强制转换?...

    从以下代码开始- byte foo = 1; byte fooFoo = foo + foo; 当我尝试编译此代码时,会得到以下错误- Error:(5, 27) java: incompatible ...

  3. java变量不声明可以直接使用吗_我们可以在不使用Java进行初始化的情况下声明最终变量吗?...

    在Java中,final是可与字段类和方法一起使用的access修饰符.当一个方法为final时,它不能被覆盖. 当变量为最终变量时,其值无法进一步修改. 当类结束时,不能扩展. 无需初始化即可声明最 ...

  4. java c static,java-是否可以禁用静态最终变量的javac内联?

    java-是否可以禁用静态最终变量的javac内联? Java静态编译器(javac)内联一些静态最终变量,并将值直接带到常量池中. 考虑以下示例. A类定义了一些常量(公共静态最终变量): publ ...

  5. java中为什么需要常量和变量的区别_Java中常量和最终变量之间的区别?

    Java中的常量 常量变量是其值固定的变量,程序中仅存在一个副本.声明常量变量并为其分配值后,就无法在整个程序中再次更改其值. 与C语言不同,Java(直接)不支持常量.但是,您仍然可以通过声明变量s ...

  6. Error:(343, 83) java: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量

    /*** 问题代码(只是为了重现错误,请忽略代码逻辑)*/ @Testpublic void test23(){ArrayList<Book> list = new ArrayList&l ...

  7. Java 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量问题解决

    问题报错: Error:(249, 93) java: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量 问题分析: 1.lambda 表达式引用的变量进行了多次赋值. 解决办法: ...

  8. Java并发知识梳理(上):并发优缺点,线程状态转换,Java内存模型,Synchronized,Volatile,final,并发三特性,Lock与AQS,ReetrandLock

    努力的意义,就是,在以后的日子里,放眼望去全是自己喜欢的人和事! 整个系列文章为Java并发专题,一是自己的兴趣,二是,这部分在实际理解上很有难度,另外在面试过程中也是经常被问到.所以在学习过程中,记 ...

  9. volatile和final

    http://www.infoq.com/cn/articles/java-memory-model-4?utm_source=infoq&utm_medium=related_content ...

  10. 如何使用 volatile, synchronized, final 进行线程间通信

    2019独角兽企业重金招聘Python工程师标准>>> 你是否真正理解并会用volatile, synchronized, final进行线程间通信呢,如果你不能回答下面的几个问题, ...

最新文章

  1. R语言使用pwr包的pwr.t.test函数对分组样本数相同的t检验进行效用分析(power analysis)、在已知效应量(effect size)、显著性水平、样本量的情况下计算假设检验的效用值
  2. invalid value encountered in double_scalars
  3. linux 无外网情况下安装 mysql
  4. 存储过程实现无限级分类(3)
  5. PostgreSQL 行变列的小应用
  6. Hadoop集群HDFS各节点磁盘使用率不平衡,使用balancer做数据平衡
  7. 图片播放器小项目(详解)
  8. Redisbook学习笔记(3)数据类型之字符串
  9. 命中书中题有奖反馈活动_三级网络技术
  10. php前台点击按钮导出excel,php上导出excel表格数据-PHP如何将查询出来的数据导出成excel表格(最好做一个按钮)...
  11. Elsevier旗下期刊利用latex模板撰写论文记录
  12. 【软件安装】IDM安装并扩展到FireFox和Google Chrome
  13. c#编程实战宝典 付强_C#开发实战宝典pdf
  14. 奥克兰大学 计算机硕士 GPA,申请奥克兰大学需要GPA成绩吗
  15. Python | pynlpir库 | pynlpir.LicenseError:Your license appears to have expired. Try running “pynlpir“
  16. ES6学习——新的语法:Temporal Dead Zone(TDZ)
  17. 猿创征文|磁盘满的本质分析——磁盘空间满与inode节点满
  18. 中文自然语言处理语言资源项目(ChineseNLPcorpus)
  19. ccc.exe_什么是CCC.exe,为什么运行?
  20. 支付宝转账银行卡收款二维码制作教程

热门文章

  1. Python 实现单例模式的一些思考
  2. GNU Linux系统变量(sysctl配置命令)综合使用
  3. C/C++中struct/union/class内存对齐
  4. hdu 1394 Minimum Inversion Number
  5. Codeforces Round 1 - 10总结 【@Abandon】
  6. “形象代言人”与“抽风式管理”
  7. VBA实战技巧精粹013:宏代码保存工作簿的3种方法
  8. 加密解密(源自Discuz!NT3.1)
  9. DHCP中继原理和配置(含常见配置配置误区)
  10. HDU--2502 月之数