`
lianpeng0011
  • 浏览: 9106 次
社区版块
存档分类
最新评论

java 并发编程二

    博客分类:
  • java
 
阅读更多

竞态条件和同步

   1、竞态条件(Race Condition) :两个以上的线程同时对一个公用的变量进行读写操作,由于执行顺序无法控制,导致结果出现错误。

 

  • 计算输出 : 比如方法调用的结果
  • 不受控制:比如线程调度是不受控制的,具有随机性
  •   顺序或时机:比如那个线程先执行,执行到哪一行代码由OS切换出去,那个线程切换进来有CPU执行

 

下面举个浅显的例子,假设有这样一个计算器:

 

public class Calculator
{
    private final int[] input;
    private int         start;
    private int         end;
    public Calculator( int start, int end )
    {
        super();
        this.input = new int[] { start, end };
        reset();
    }
    private void reset()
    {
        start = input[0];
        end = input[1];
    }
    public int add()
    {
        int res = 0;
        for ( ; start <= end; start++ )
            res += start;
        reset();
        return res;
    }
}

 我们期望它每次计算都能输出正确的结果,下面有两个线程连续调用计算器100次:

 

 

private static class CalculateThread extends Thread
{
    private Calculator calculator;
 
    public CalculateThread( Calculator calculator )
    {
        super();
        this.calculator = calculator;
    }
 
    public void run()
    {
        for ( int i = 0; i < 1000; i++ )
            System.out.println( calculator.add() );
    }
}
 
public static void main( String[] args )
{
    Calculator calculator = new Calculator( 0, 100 );
    new CalculateThread( calculator ).start(); //线程1
    new CalculateThread( calculator ).start(); //线程2
}

 观察计算结果,可以看到,大部分的调用能够得到正确的和5050,但是会有一些随机的值夹杂其中,例如0、4189、1766…… 而且每次运行的输出结果都不一样。这就是典型的竞态条件场景了,线程1在调用add()方法时,随时可能因为OS的线程调度而暂停运行,这个时候calculator处于一种不一致的中间状态,此时线程2来发起/继续它的add()调用,calculator的两个状态字段的值是不确定的、随机的,因而计算结果必然不可靠。

 

之所以大部分结果是正确的,是因为CPU分配给线程的时间片相对于add()中的循环来说,是非常漫长的,因此大部分情况下线程能够不受干扰的一次执行完这个方法。

即使是一些非常简单的代码,也可能引入竞态条件:

 

public class IntCounterTest {
    private static int counter = 0;
    private static class CounterThread extends Thread {
        public void run() {
            for (int i = 0; i < 10000000; i++)
                synchronized (this) {
                    counter++;
                }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        CounterThread t1 = new CounterThread();
        t1.start();
        CounterThread t2 = new CounterThread();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter);
    }
}

 上面的例子中,我们让t1、t2两个线程分别把计数器的值增加1000万,期望结果应该是2000万,但是几乎每次运行结果都不正确。 什么原因呢?因为++操作符至少包含三条指令:

 

 

  •     从main memory 中读取变量到local memory
  •     进行变量计算
  •     将结果更新到main memory

  出现竞态条件的根本原因是一项操作(例如一个方法调用、一次计算)不是原子(atomic)操作。

    2、synchronize

   是让一个java的方法编程原子的,那么就需要线程在执行这个方法时不受干扰,即其他线程不会来修改共享内存的状态。达到这一目的就是采用互斥锁(Mutex Lock) 机制,该机制可以保持同一时刻只有一个线程对关键的、修改共享资源状态的代码具有访问权。Java语言支持互斥锁机制--synchronize关键字,我们可以对上例的代码进行改写:

private static class CounterThread extends Thread
{
    public void run()
    {
        for ( int i = 0; i < 10000000; i++ )
            //限制同一时刻只有一个人能访问对象IntCounterTest.class
            //由于这个类对象是全局唯一的,因此同一时刻只有一个线程能对counter进行递增
            synchronized ( IntCounterTest.class )
            {
                counter++;
            } //在同步块的边界,所有寄存器变量的值被视为无效
    }
}

 这样再执行,结果就总是2000万了。不过使用synchronized的代价是高昂的,在我机器上测试,修改前后代码运行耗时相差超过100倍。Java5提供的AtomicInteger性能相对较好(仍然有10倍性能差距),可以用来代替上例的int。

等待(阻塞)在synchronized块入口的线程,和sleep的线程、wait的线程,从Linux系统的角度来说,都处于S(可中断睡眠)状态。

    volatile

    在Java中除了long、double以外变量的存储和读取都是原子操作,因此这些变量的读取和存储不会有中间状态。即便如此,没有受保护的共享变量任然是不安全的,因为Java的内存模型允许线程持有共享变量的本地内存(Local memory,往往是寄存器) 拷贝,这就意味着,一个线程修改了某些共享变量后,其他线程可能不会直接发现。解决这个问题有两种方式:

  1.     禁止变量直接访问,使用synchronize关键字对getter/setter 方法进行保护
  2.     变量使用volatile生命变量

   第二种方式更加的优雅,volatile可以理解为对单个变量的读写进行同步(JSR-133),volatile关键字包括:

   可见性 : 确保没次变量的读都去主内存

   原子性:  确保没次变量的写都直接更改住内存

   有序性: Happens-Before原则,对volatile写操作必须发生在后续读操作后。

 

    volatile关键字可以用在各种变量上,甚至是数组和对象,但是后两者,volatile定义的作用域是在引用(指针)上。

分享到:
评论

相关推荐

    java并发编程2

    Java并发编程是Java开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。以下是对标题和描述中所提及的几个知识点的详细解释: 1. **线程与并发** - **线程*...

    java并发编程实战源码,java并发编程实战pdf,Java

    《Java并发编程实战》是Java并发编程领域的一本经典著作,它深入浅出地介绍了如何在Java平台上进行高效的多线程编程。这本书的源码提供了丰富的示例,可以帮助读者更好地理解书中的理论知识并将其应用到实际项目中。...

    Java 并发编程实战.pdf

    《Java并发编程实战》这本书是关于Java语言中并发编程技术的经典著作。它详细介绍了如何在Java环境中有效地实现多线程程序和并发控制机制。在Java平台上,由于其本身提供了强大的并发编程支持,因此,掌握并发编程...

    《java 并发编程实战高清PDF版》

    《Java并发编程实战》是一本深入探讨Java平台并发编程的权威指南。这本书旨在帮助开发者理解和掌握在Java环境中创建高效、可扩展且可靠的多线程应用程序的关键技术和实践。它涵盖了从基本概念到高级主题的广泛内容,...

    JAVA并发编程艺术 高清pdf

    JAVA并发编程艺术 高清pdf : 1.并发变成的挑战 2. java并发机制的底层实现原理 3. java 内存模型 4. java并发编程基础 5.java中的锁。。。。。。。

    java 并发编程的艺术pdf清晰完整版 源码

    《Java并发编程的艺术》这本书是Java开发者深入理解并发编程的重要参考书籍。这本书全面地介绍了Java平台上的并发和多线程编程技术,旨在帮助开发者解决在实际工作中遇到的并发问题,提高程序的性能和可伸缩性。 ...

    Java并发编程实战华章专业开发者书库 (Tim Peierls 等 美Brian Goetz).pdf

    《Java并发编程实战》是一本深入探讨Java平台并发编程的权威指南,由Tim Peierls等人与Brian Goetz合著,旨在帮助Java开发者理解和掌握在多线程环境中编写高效、安全的代码。这本书由拥有丰富经验的JDK并发大师及...

    java并发编程实战中文加英文版加源码

    JAVA并发编程实践中文版 英文版 原书源码 带书签 java_concurrency_in_practice.pdf 英文版还是不错的,但是中文版的译者典型的没有技术功底,介绍上说什么专家, 翻译的非常差劲,有些句子都不通顺,都不知道自己去...

    java并发编程内部分享PPT

    Java并发编程是Java开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。这份“java并发编程内部分享PPT”显然是一个深入探讨这一主题的资料,旨在帮助开发者...

    java并发编程实战(英文版)

    ### Java并发编程实战知识点概述 #### 一、Java并发特性详解 在《Java并发编程实战》这本书中,作者深入浅出地介绍了Java 5.0和Java 6中新增的并发特性。这些特性旨在帮助开发者更高效、安全地编写多线程程序。书中...

    java并发编程书籍

    2. **同步机制**:Java并发编程的核心在于同步,以防止数据不一致性和资源竞争。`synchronized`关键字用于实现临界区的互斥访问,确保同一时刻只有一个线程执行特定代码块。此外,还有`wait()`, `notify()`, `...

    Java并发编程实践高清pdf及源码

    《Java并发编程实践》是一本深入探讨Java多线程编程的经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowles和David Holmes等专家共同编写。这本书全面介绍了Java平台上的并发编程技术,是Java开发...

    java并发编程实践pdf笔记

    Java并发编程实践是Java开发中不可或缺的一个领域,它涉及到如何高效、正确地处理多线程环境中的任务。这本书的读书笔记涵盖了多个关键知识点,旨在帮助读者深入理解Java并发编程的核心概念。 1. **线程和进程的...

    JAVA并发编程艺术pdf版

    《JAVA并发编程艺术》是Java开发者深入理解和掌握并发编程的一本重要著作,它涵盖了Java并发领域的核心概念和技术。这本书详细阐述了如何在多线程环境下有效地编写高效、可靠的代码,对于提升Java程序员的技能水平...

    JAVA并发编程实践.pdf+高清版+目录 书籍源码

    《JAVA并发编程实践》这本书是Java开发者深入理解并发编程的重要参考资料。它涵盖了Java并发的核心概念、工具和最佳实践,旨在帮助读者在多线程环境下编写高效、安全的代码。 并发编程是现代软件开发中的关键技能,...

    Java并发编程从入门到精通(pdf)(附源码)

    《Java并发编程从入门到精通》是一本专为Java开发者设计的深度学习并发编程的书籍。作者韩剑锋,凭借其12年的IT行业经验,曾担任多家IT公司的研发总监和技术总监,以其丰富的实战经验和深厚的理论知识,为读者提供了...

    java并发编程与实践

    "Java并发编程与实践"文档深入剖析了这一主题,旨在帮助开发者理解和掌握如何在Java环境中有效地实现并发。 并发是指在单个执行单元(如CPU)中同时执行两个或更多任务的能力。在Java中,这主要通过线程来实现,...

    Java并发编程书籍高清版

    本资源包含三本权威的Java并发编程书籍:《Java并发编程实践》、《java并发编程的艺术》以及Brian Goetz的文字版《Java并发编程实践》。 首先,我们来看《Java并发编程实践》(Java Concurrency in Practice)这...

    java 并发编程2

    Java并发编程是Java开发中的重要领域,特别是在多核处理器和分布式系统中,高效地利用并发可以极大地提升程序的性能和响应速度。《Java并发编程实践》(Addison.Wesley.Java.Concurrency.in.Practice.May.2006.chm)...

Global site tag (gtag.js) - Google Analytics