Java并发编程学习全解.docVIP

  • 5
  • 0
  • 约9.94千字
  • 约 11页
  • 2017-01-26 发布于湖北
  • 举报
Java并发编程学习全解

Java并发编程学习 一 对象的共享 线程之间对象的共享不仅仅需要有原子性和临界区,还有一个重要方面:内存可见性 1 可见性 读操作的线程并非可以一直获取到写线程写入的最新值,例如: 代码示例 private static boolean ready; private static int number; private static class ReaderThread extends Thread { public void run() { while (!ready) Thread.yield(); System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; } 以上实例代码可能存在如下问题: 一直循环下去,因为读线程可能看不到写线程写入了ready。 在读线程中输出0,因为主线程可能对number和ready的赋值顺序进行了改变。 这是由于线程之间没有正确使用同步使得数据在多个线程中共享出现错误导致的。常见的问题包括: 读取到的数据已经失效 没有对64位数据同步读写 失效的数据 以上述例子来说,当读线程读取ready变量的时候,很可能该变量已经失效。失效的数据通常包括两类数据: 值数据 引用数据 值数据的失效例如一个计数器应用,如果不对count变量进行同步处理的话可能会导致计数不准的问题。如果对象的引用失效可能会导致一些莫名其妙的问题,比如意料之外的异常,被破坏的数据结果,不精确的计算及无限循环等。 非原子的64位操作 根据Java内存模型的要求,变量的读写操作必须是原子操作。对于非volatile类型的long和double变量,JVM允许将64位读写操作分解为两个32为的操作,这样在多线程环境中共享可变的long和double变量也是不安全的。 一般的解决方式可以通过加锁或者声明变量为volatile变量。 内置锁可以用于确保某个线程以一种可预测的方式看到另一个线程执行的结果。加锁的含义不仅仅局限于互斥行为,还包括了内存可见性。 Java同时提供了一种较弱的同步机制,即volatile变量。volatile变量不同于加锁机制,加锁机制可以保证原子性和可见性,但是volatile只能保证可见性。volatile变量会确保将变量的更新结果通知到其他线程,被声明volatile变量会有两个效果: 不会对该变量重排序; 不会缓存在寄存器或者其他处理器不可见的地方。 使用volatile变量可以不执行加锁操作,也不会阻塞线程,开销要比同步要低。但是只能确保可见性,不能确保原子性,更不能保证在volatile变量递增操作的原子性,在使用的时候要注意。当满足以下所有条件时才应该使用volatile变量: 对变量写入不依赖当前值,或者确保只有单个线程更新变量的值; 该变量不被要求与其他变量同步变化; 在访问该变量时不需要加锁,因为加上锁就没必要使用volatile了。 2 发布与逸出 发布一个对象是指使一个对象能在当前作用域之外的代码中使用。一般是将成员变量或静态变量公开。 对于一个类的外部方法是不完全由该类规定的方法,包括该类以外定义的方法及由该类定义但可以被改写的方法。当把一个对象传递给一个外部方法,就相当于发布了这个对象。 逸出一个对象是指某个不应该发布的对象被发布 安全构造对象 错误的构造对象方式 public ThisEscape(EventSource source) { source.registerListener(new EventListener() { public void onEvent(Event e) { doSomething(e); } }); } 在EventListener中保留了this的引用,因此会将this引用逸出。 一定不要在构造过程中使this引用逸出。在构造对象的过程中常见的问题: 构造函数中将自己的引用传递给一个线程并将该线程启动; 构造函数中调用一个可被改写的方法 如果构造函数中需要注册事件监听器或者启动线程,一个办法是将构造函数私有化,用工厂模式初始化。例如

文档评论(0)

1亿VIP精品文档

相关文档