`

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
  • 大小: 21.1 KB
  • 大小: 201.2 KB
  • 大小: 44.7 KB
分享到:
评论

相关推荐

    狂神说Java-多线程课程全部代码.rar

    《狂神说Java-多线程课程全部代码》是一个涵盖了Java多线程和并发编程的实战教程资源。这个压缩包包含了一系列的示例代码(如demo01),旨在帮助开发者深入理解和掌握Java中的多线程技术及其在并发环境中的应用。 ...

    java核心技术第八版源代码(全)

    1.4 Java发展简史 1.5 关于Java的常见误解 第2章 Java程序设计环境 第3章 Java基本的程序设计程序 第4章 对象与类 第5章 继承 第6章 接口与内部类 第7章 图形程序设计 第8章 事件处理 第9章 Swing用户界面组件 第10...

    [java.核心技术.第八版].Core.Java..8th.Edition源代码 示例代码

    1.4 Java发展简史 1.5 关于Java的常见误解 第2章 Java 程序设计环境 第3章 Java基本的程序设计程序 第4章 对象与类 第5章 继承 第6章 接口与内部类 第7章 图形程序设计 第8章 事件处理 第9章 Swing用户界面组件 第...

    java 基础学习PPT

    Java是一种广泛使用的编程语言,以其平台独立性、面向对象的设计、简洁性、健壮性、安全性、解释性、多线程处理能力和动态特性著称。自1995年由Sun Microsystems公司发布以来,Java经历了多次重要更新,持续推动其...

    Java核心技术 卷I(原书第8版).Part1 pdf

    1.4 Java发展简史 1.5 关于Java的常见误解 第2章 Java程序设计环境 第3章 Java基本的程序设计程序 第4章 对象与类 第5章 继承 第6章 接口与内部类 第7章 图形程序设计 第8章 事件处理 第9章 Swing用户界面组件 第10...

    Java核心技术 卷I(原书第8版).part2 PDF

    1.4 Java发展简史 1.5 关于Java的常见误解 第2章 Java程序设计环境 第3章 Java基本的程序设计程序 第4章 对象与类 第5章 继承 第6章 接口与内部类 第7章 图形程序设计 第8章 事件处理 第9章 Swing用户界面组件 第10...

    Core Java. Volume I. Fundamentals, 8th Edition JAVA核心技术1基础知识

    1.4 Java发展简史 1.5 关于Java的常见误解 第2章 Java程序设计环境 第3章 Java基本的程序设计程序 第4章 对象与类 第5章 继承 第6章 接口与内部类 第7章 图形程序设计 第8章 事件处理 第9章 Swing用户界面组件 第10...

    大数据必学Java基础(一):Java体系结构、特性和优势

    Java是Sun公司开发的一种高级编程语言,具有跨平台、安全、面向对象、简单、高性能、分布式、多线程和健壮性等特性。Java的历史可以追溯到1991年,当时James Gosling率领的Sun公司工程师小组想要设计一种小型计算机...

    java初学者教程ppt

    Java的发展简史始于1991年,由SUN Microsystems公司的James Gosling、Bill Joe等人在开发名为Oak的软件时诞生。虽然最初目标并未成功,但 Oak 后来演变为Java,迅速获得了广泛的关注。Java因其特性被众多著名公司...

    java基础讲义

    Java的特性和优势包括自动内存管理(垃圾回收)、多线程支持、丰富的类库以及强大的异常处理机制,这些特性使得Java在各种应用场景中表现出色。 了解Java应用程序的运行机制至关重要。Java程序在运行时需要JRE...

    java简介,关于Java入门方面的

    Java的发展简史表明,其名称来源于印度尼西亚的一个岛屿,同时又与程序员喜爱的咖啡相关。Java在互联网时代的成功主要归功于其跨平台特性,允许程序在不同操作系统上运行而无需重新编译。自1995年发布以来,Java已经...

    JAVA课设\课程设计说明书

    1. **JAVA语言发展简史** - JAVA起初名为Oak,由Sun公司的Green Team小组创建,最初设计用于嵌入式系统。 - 1994年,随着互联网的兴起,Oak被重新定位并更名为JAVA,适应网络环境,因其在网络上的优势迅速获得广泛...

    java基础培训.pdf(看这份就够了)

    2. Java语言特性:Java是一门简单、面向对象、健壮、安全、解释执行、与平台无关、支持多线程和动态的编程语言。这些特性让Java成为开发企业级应用和移动应用的理想选择。 3. Java程序运行机制:Java程序通过Java...

    JAVA基础课程讲义

    JAVA的发展简史可追溯到1995年,由Sun Microsystems推出,其设计理念是“一次编写,到处运行”。JAVA之所以流行,是因为它具有跨平台性、安全性和高性能,同时提供了丰富的类库。JAVA分为多个版本,如JDK(Java ...

    Java培训资料

    - **多线程**:Java内置了对多线程的支持,可以轻松地开发出高性能的应用程序。 - **动态性**:Java具有高度动态性,可以通过反射机制在运行时获取类的信息,并通过动态代理等方式实现动态绑定。 - **体系结构中立...

    java基础知识大全(强烈推荐).docx共66页,多年培训经验整理的实用Java知识点

    - **多线程能力**:Java的多线程模型允许开发者编写高效且响应迅速的应用程序,尤其是在多核处理器环境中。 - **操作系统特定的多线程机制**:虽然Java本身提供了跨平台的多线程支持,但是底层的线程实现会根据不同...

Global site tag (gtag.js) - Google Analytics