首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Java多线程发展简史(四)

2012-12-24 
Java多线程发展简史(4)JDK 6.0JDK 6.0对锁做了一些优化,比如锁自旋、锁消除、锁合并、轻量级锁、所偏向等。在这

Java多线程发展简史(4)
JDK 6.0

JDK 6.0对锁做了一些优化,比如锁自旋、锁消除、锁合并、轻量级锁、所偏向等。在这里不一一介绍,但是给一个例子以有感性认识:

import java.util.Vector;     public class LockElimination {      public String getStr() {          Vector v = new Vector();          v.add(3);          v.add(4);          return v.toString();      }             public static void main(String[] args) {          System.out.println(new LockElimination().getStr());      }  } 

在这个例子中,对vector的加锁完全是没有必要的,这样的锁是可以被优化消除的。

CyclicBarrier是JDK 6.0新增的一个用于流程控制的类,这个类可以保证多个任务在并行执行都完成的情况下,再统一执行下一步操作:


上面这个例子就模拟了,两个子任务(分别执行2000毫秒和4000毫秒)完成以后,再执行一个总任务(2000毫秒)并打印完成。

还有一个类似的类是CountDownLatch(使用倒数计数的方式),这样的类出现标志着,JDK对并发的设计已经逐步由微观转向宏观了,开始逐步重视并发程序流程,甚至是框架上的设计,这样的思路我们会在下文的JDK 7.0中继续看到。
import java.util.concurrent.BrokenBarrierException;  import java.util.concurrent.CyclicBarrier;     public class BarrierUsage extends Thread {      private static CyclicBarrier barrier = new CyclicBarrier(2, new Thread() {          public void run() {              try {                  Thread.sleep(2000);              } catch (InterruptedException e) {              }              System.out.println("finish");          };      });         private final int sleepMilSecs;         public BarrierUsage(int sleepMilSecs) {          this.sleepMilSecs = sleepMilSecs;      }         @Override     public void run() {          try {              Thread.sleep(sleepMilSecs);              System.out.println(sleepMilSecs + " secs slept");              barrier.await();          } catch (InterruptedException e) {          } catch (BrokenBarrierException e) {          }      }         public static void main(String[] args) {          new BarrierUsage(2000).start();          new BarrierUsage(4000).start();      }  } 

JDK 7.0

2011年的JDK 7.0进一步完善了并发流程控制的功能,比如fork-join框架:


把任务分解成不同子任务完成;比如Phaser这个类,整合了CyclicBarrier和CountDownLatch两个类的功能,但是提供了动态修改依赖目标的能力;还有NIO2的新开放特性。这里不详细介绍了。

Java的未来

在多线程编程方面,Java的未来会怎样?

JDK 8.0按计划将在2013年夏天发布,Java从动态语言那里学了很多过来,比如闭包等等,在多线程方面会怎样呢?郁于JLS所限,无法有大的突破,还是有另辟蹊径的办法?纵观整个Java发展的历程,都在努力修正多线程模型实现上的种种弊端,尽可能在保留虚拟机优化特性的基础上给使用者屏蔽细节。

在来回想一下Java最基础的线程模型,其他语言是怎样实现的呢?

比如C#,任何类的任何方法,都可以成为线程的执行方法:
using System;  using System.Threading;     public class AnyClass {      public void DoSth() {           Console.WriteLine("working");      }  }     class ThreadTest{      public static void Main() {          AnyClass anyClass = new AnyClass();          ThreadStart threadDelegate = new ThreadStart(anyClass.DoSth);          Thread myThread = new Thread(threadDelegate);                     myThread.Start();      }  } 

上面的AnyClass的DoSth方法,就模拟线程执行打印了一句话。

再来看一门小众语言Io,在语法糖的帮助下,实现更加简单:
thread := Object clone  thread start := method("working" println)  thread @@start 

因为Io是基于原型的语言(如果你有兴趣的话,可以在我的blog里找到Io介绍),通过这样的@符号,就实现了新启一个线程运行的功能。

再来看看JDK 5.0的ReentrantLock类,它完全实现了synchronized语义上的全部功能,并且还能具备诸如条件锁、锁超时、公平锁等等更优越的特性(特别值得一提的是tryLock的功能也实现了,就是说可以判定假如这个时间获取锁是否能够成功),甚至在并发量居高不下时,性能还更加优越……我不禁要问,用一个Java实现的锁类去从功能上代替一个已有的同步关键字,这岂不是Java自己在抽自己嘴巴?
import java.util.concurrent.locks.ReentrantLock;     public class ReentrantLockUsage implements Runnable {         private static ReentrantLock lock = new ReentrantLock();         @Override     public void run() {          lock.lock();             try {              System.out.println("do something 1");              Thread.sleep(2000);          } catch (InterruptedException e) {          } finally {              lock.unlock(); // Why put it in finally block?          }             System.out.println("finish 1");      }         public static void main(String[] args) {          new Thread(new ReentrantLockUsage()).start();          lock.lock();             try {              System.out.println("do something 2");              Thread.sleep(2000);          } catch (InterruptedException e) {          } finally {              lock.unlock();          }             System.out.println("finish 2");      }  } 

其实这个问题渊源已久,JLS在最初把Java锁机制(包括synchronized关键字)定得太死,以至于无法在上面做进一步的修正和优化,无奈只好另外重新建一个类来做这些未竟的事情。如果让Jame Gosling重新回到二十多年前,他也许会选择不同的实现。

关于协程(coroutine)。很多语言都内置了对协程的实现(协程的含义请自行查阅维基百科),听起来似乎是一个崭新的名字,但是其实这个概念一点都不新,JavaScript引擎对单个页面的解析就是通过协程的方式在一个线程内完成的。协程的实现困难有两点,一个是异常的处理,一个是出入线程时现场(包括堆栈)的保存和设置。有一些开源库已经有了Java上协程的实现,如果你感兴趣的话,不妨关注Kilim和Coroutine for Java。

最后,让我们来回顾一下Java多线程发展的历史。从Java诞生到如今有二十年了,可未来会怎样,又谁知道呢?


补充2012-09-18:

jinnianshilongnian回复:

一、我觉得非原子性的++操作这句话有点模糊,如下所示:

1、nonAtomicCounter++; 不是原子的原因是因为它是静态/实例变量,需要 读/操作/写对象成员变量。可以加把锁保证读/操作/写原子性

synchronized(atomicCounter) {

nonAtomicCounter++;

}


2、如果nonAtomicCounter++; 是局部变量 仅有一条指令 iinc i,1;但局部变量又不会线程不安全;

3、nonAtomicCounter如果是long(64位)的在32位机器即使是局部变量也是线程不安全的(四火补充:在64位机器上也不是线程安全的);

4、Atomic×××等类通过Unsafe的compareAndSwap××× 即CAS完成的。

应该是使用成员变量的++时。


二、对于文中这段话:

但是,上面的情况是对boolValue使用volatile修饰保证其可见性的情况下出现的,如果不对boolValue使用 volatile修饰,运行时就一次不会出现(起码在我的电脑上)打印“WTF!”的情形,换句话说,这反而是不太正常的,我无法猜测JVM做了什么操作,基本上唯一可以确定的是,没有用volatile修饰的时候,boolValue在获取的时候,并不能总取到最真实的值。

这个应该是工作内存 和 主内存 同步的问题。 用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。

但[boolValue == !boolValue] 和 check/swap 操作并不是原子操作。

也可以通过 在check/swap的两个boolValue加锁来保证同步

synchronized(this) { 

      boolValue = !boolValue; 

}
三、对于DCL问题那段代码,网上也有文章说即使使用volatile也不能保证DCL的安全性:

http://www.ibm.com/developerworks/java/library/j-dcl/index.html

四、说明:

你给的ibm那个文章链接也说到,“The memory model allows what is known as "out-of-order writes" and is a prime reason why this idiom fails.” 所以根因在于out of order writes,引起的问题是“partially initialized”,但是文章里面提到使用volatile不能解决问题的原因在于一些JVM的实现并不能保证顺序一致性,换句话说,对于 happens-before守则并没有严格遵守,且不说他的说法是否有根据,我谈论这个问题的时候一定是在JLS明确规定以下进行的。至于虚拟机实现上的问题,我不得而知。

FYI:

http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

另外对于element前面如果不加volatile/final的话,也不能保证解决DCL问题,这里四火做一个说明:

在于instance的可见性由volatile保证了,可是element的name并没有任何语义上的保证,这里可以使用 volatile,但是对于不可变对象其实也可以使用在这里语义弱一些的final,也是可以解决问题的,JSR133对这两个关键字的语义做了强化,我上面给的链接里面也提到了,“the values assigned to the final fields in the constructor will be visible to all other threads without synchronization”。

感谢讨论。

原文链接:http://www.raychase.net/?p=698
ref:http://developer.51cto.com/art/201209/357617_3.htm

热点排行