网站关键词优化排名软件,南阳注册公司多少钱,百度下载安装官方下载,优优群排名优化软件同步有两种属性#xff1a;互斥性和可见性。synchronized关键字与两者都有关系。Java同时也提供了一种更弱的、仅仅包含可见性的同步形式#xff0c;并且只以volatile关键字关联。假设你自己设计了一个停止线程的机制(因为无法使用Thread不安全的stop()方法))。清单1中Thread…同步有两种属性互斥性和可见性。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。照下面编译清单2javac 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 practiceSafe construction techniques》学习更多常见线程风险的相关知识。本文节选自《Java线程与并发编程实践》Java线程和并发工具是应用开发中的重要部分备受开发者的重视也有一定的学习难度。本书是针对Java 8中的线程特性和并发工具的快速学习和实践指南。全书共8章分别介绍了Thread类和Runnable接口、同步、等待和通知、线程组、定时器框架、并发工具、同步器、锁框架以及高级并发工具等方面的主题。每章的末尾都以练习题的方式帮助读者巩固所学的知识。附录A给出了所有练习题的解答附录B给出了一个基于Swing线程的教程。本书适合有一定基础的Java程序员阅读学习尤其适合想要掌握Java线程和并发工具的读者阅读参考。