Java 线程第三版 第三章数据同步 读书笔记

多线程间共享数据问题

一、Synchronized关键字

atomic一词与“原子”无关,它曾经被认为是物质的最小的单元,不能再被拆解成更小的部分。

当一个方法被声明成synchronized,要执行此方法的thread必须先取得一个token,我们将它称为锁。一旦该方法取得(或者说是获得)锁,它将运行此方法然后释放掉(或者返回)此锁。不管方法时怎样返回的(包括通过异常)该锁会被释放。

二、Volatile关键字

如果变量被标示为volatile,每次使用该变量时都必须从主寄存器中读出。相同地,每次要写入该变量时,值都必须存入主寄存器。更进一步,Java指定对volatile变量的加载与存储都是atomic的,无论是否是long与double变量。

volatile声明的变量进行++、--操作不能保证原子性。

volatile声明的数组,会让数组的引用变成volatile数组中的元素不是volatile。

三、更多竞态条件的讨论

public class ScoreLabel extends JLabel implements CharacterListener {

    private volatile int score = 0;
    private int char2type = -1;
    private CharacterSource generator = null, typist = null;

    public ScoreLabel (CharacterSource generator, CharacterSource typist) {
        this.generator = generator;
        this.typist = typist;

        if (generator != null)
            generator.addCharacterListener(this);
        if (typist != null)
             typist.addCharacterListener(this);
    }

    public ScoreLabel () {
        this(null, null);
    }

    public synchronized void resetGenerator(CharacterSource newGenerator) {
        if (generator != null)
            generator.removeCharacterListener(this);
        generator = newGenerator;
        if (generator != null)
            generator.addCharacterListener(this);
    }

    public synchronized void resetTypist(CharacterSource newTypist) {
        if (typist != null)
            typist.removeCharacterListener(this);
        typist = newTypist;
        if (typist != null)
            typist.addCharacterListener(this);
    }

    public synchronized void resetScore() {
        score = 0;
        char2type = -1;
        setScore();
    }

    private void setScore() {
        // This method will be explained later in chapter 7
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                setText(Integer.toString(score));
            }
        });
    }

    public synchronized void newCharacter(CharacterEvent ce) {
        // Previous character not typed correctly - 1 point penalty
        if (ce.source == generator) {
            if (char2type != -1) {
                score--;
                setScore();
            }
            char2type = ce.character;
        }

        // If character is extraneous - 1 point penalty
        // If character does not match - 1 point penalty
        else {
            if (char2type != ce.character) {
                score--;
           } else {
               score++;
               char2type = -1;
           }
           setScore();
       }
    }
}

此类共享的数据是由实际的得分数、需要被输入的字母与少数持有的字母来源作为登记用的变量等所组成。解决竞态条件问题意味着让这些数据在正确的scope中被同步化。

如果newCharacter方法不能确保同步,其中包含的变量char2type、score变量的修改并不能保证在所有的线程中都能实时的获取到正确的最后一次修改的值,导致基于char2type的判断出现问题,接连导致score也出现问题。

解决办法是在此类中所有涉及到这两个变量的都把当前类作为同步锁(即每个方法都添加一个synchronized关键字),目的是在所有线程中调用这些方法都必须要是互斥操作,不可能同时多个线程调用操作这两个变量的方法,从而保证正确性。

四、显示锁

public class ScoreLabel extends JLabel implements CharacterListener {

    private volatile int score = 0;
    private int char2type = -1;
    private CharacterSource generator = null, typist = null;
    private Lock scoreLock = new ReentrantLock();

    public ScoreLabel (CharacterSource generator, CharacterSource typist) {
        this.generator = generator;
        this.typist = typist;

        if (generator != null)
            generator.addCharacterListener(this);
        if (typist != null)
             typist.addCharacterListener(this);
    }

    public ScoreLabel () {
        this(null, null);
    }

    public void resetGenerator(CharacterSource newGenerator) {
        try {
            scoreLock.lock();
            if (generator != null)
                generator.removeCharacterListener(this);

            generator = newGenerator;
            if (generator != null)
                generator.addCharacterListener(this);
        } finally {
            scoreLock.unlock();
        }
    }

    public void resetTypist(CharacterSource newTypist) {
        try {
            scoreLock.lock();
            if (typist != null)
                typist.removeCharacterListener(this);

            typist = newTypist;
            if (typist != null)
                typist.addCharacterListener(this);
        } finally {
            scoreLock.unlock();
        }
    }

    public void resetScore() {
        try {
            scoreLock.lock();
            score = 0;
            char2type = -1;
            setScore();
        } finally {
            scoreLock.unlock();
        }
    }

    private void setScore() {
        // This method will be explained later in chapter 7
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                setText(Integer.toString(score));
            }
        });
    }

    public void newCharacter(CharacterEvent ce) {
        try {
            scoreLock.lock();
            // Previous character not typed correctly - 1 point penalty
            if (ce.source == generator) {
                if (char2type != -1) {
                    score--;
                    setScore();
                }
                char2type = ce.character;
            }

            // If character is extraneous - 1 point penalty
            // If character does not match - 1 point penalty
            else {
                if (char2type != ce.character) {
                    score--;
                } else {
                    score++;
                    char2type = -1;
                }
                setScore();
            }
        } finally {
            scoreLock.unlock();
        }
    }
}

与上一个例子原理相同,都是涉及char2type、score两个变量修改的方法上都添加锁,当前例子仅是使用另外一种语法使用提供的Lock与unLock来加锁解锁操作,在此例子上这两种做法是等价的。之后会讨论synchronized与Lock的不同之处。

五、Lock Scope

public class ScoreLabel extends JLabel implements CharacterListener {

    private volatile int score = 0;
    private int char2type = -1;
    private CharacterSource generator = null, typist = null;
    private Lock scoreLock = new ReentrantLock();

    public ScoreLabel (CharacterSource generator, CharacterSource typist) {
        this.generator = generator;
        this.typist = typist;

        if (generator != null)
            generator.addCharacterListener(this);
        if (typist != null)
             typist.addCharacterListener(this);
    }

    public ScoreLabel () {
        this(null, null);
    }

    public void resetGenerator(CharacterSource newGenerator) {
        try {
            scoreLock.lock();
            if (generator != null)
                generator.removeCharacterListener(this);

            generator = newGenerator;
            if (generator != null)
                generator.addCharacterListener(this);
        } finally {
            scoreLock.unlock();
        }
    }

    public void resetTypist(CharacterSource newTypist) {
        try {
            scoreLock.lock();
            if (typist != null)
                typist.removeCharacterListener(this);

            typist = newTypist;
            if (typist != null)
                typist.addCharacterListener(this);
        } finally {
            scoreLock.unlock();
        }
    }

    public void resetScore() {
        try {
            scoreLock.lock();
            score = 0;
            char2type = -1;
            setScore();
        } finally {
            scoreLock.unlock();
        }
    }

    private void setScore() {
        // This method will be explained later in chapter 7
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                setText(Integer.toString(score));
            }
        });
    }

    public void newCharacter(CharacterEvent ce) {
        if (ce.source == generator) {
            try {
                scoreLock.lock();
                // Previous character not typed correctly - 1 point penalty
                if (char2type != -1) {
                    score--;
                    setScore();
                }
                char2type = ce.character;
            } finally {
                scoreLock.unlock();
            }
        }
        // If character is extraneous - 1 point penalty
        // If character does not match - 1 point penalty
        else {
            try {
                scoreLock.lock();
                if (char2type != ce.character) {
                    score--;
                } else {
                    score++;
                    char2type = -1;
                }
                setScore();
            } finally {
                scoreLock.unlock();
            }
        }
    }
}

Lock与unLock可以放到自己需要的任何地方。

六、Synchronized块

public class ScoreLabel extends JLabel implements CharacterListener {

    private volatile int score = 0;
    private int char2type = -1;
    private CharacterSource generator = null, typist = null;

    public ScoreLabel (CharacterSource generator, CharacterSource typist) {
        this.generator = generator;
        this.typist = typist;

        if (generator != null)
            generator.addCharacterListener(this);
        if (typist != null)
             typist.addCharacterListener(this);
    }

    public ScoreLabel () {
        this(null, null);
    }

    public synchronized void resetGenerator(CharacterSource newGenerator) {
        if (generator != null)
            generator.removeCharacterListener(this);
        generator = newGenerator;
        if (generator != null)
            generator.addCharacterListener(this);
    }

    public synchronized void resetTypist(CharacterSource newTypist) {
        if (typist != null)
            typist.removeCharacterListener(this);
        typist = newTypist;
        if (typist != null)
            typist.addCharacterListener(this);
    }

    public synchronized void resetScore() {
        score = 0;
        char2type = -1;
        setScore();
    }

    private void setScore() {
        // This method will be explained later in chapter 7
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                setText(Integer.toString(score));
            }
        });
    }

    public void newCharacter(CharacterEvent ce) {
        // Previous character not typed correctly - 1 point penalty
        if (ce.source == generator) {
            synchronized(this) {
                if (char2type != -1) {
                    score--;
                    setScore();
                }
                char2type = ce.character;
            }
        }

        // If character is extraneous - 1 point penalty
        // If character does not match - 1 point penalty
        else {
            synchronized(this) {
                if (char2type != ce.character) {
                    score--;
               } else {
                   score++;
                   char2type = -1;
               }
               setScore();
            }
        }
    }
}

在此例子中,被锁住的对象与用在方法的同步化上的是同一个对象:this对象。

七、选择Locking机制

synchronized与Lock在静态方法(static method)上有所区别,因为在方法上使用synchronized是针对当前对象锁定,而静态方法是全局的,使用这种办法会使确保正确性增加难度,相反使用Lock因为它与当前对象无关,只需要在方法内设置lock与unlock所以更容易确保多线程同步的正确性。

八、Lock Interface

boolean tryLock()

仅在调用时锁为空闲状态才获取该锁。

如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。

此方法的典型使用语句如下:

Lock lock = ...;

if (lock.tryLock()) {

try {

// manipulate protected state

} finally {

lock.unlock();

}

} else {

// perform alternative actions

}

此用法可确保如果获取了锁,则会释放锁,如果未获取锁,则不会试图将其释放。

返回:

如果获取了锁,则返回 true;否则返回 false。

九、Nested Lock

public class ScoreLabel extends JLabel implements CharacterListener {

    private volatile int score = 0;
    private int char2type = -1;
    private CharacterSource generator = null, typist = null;

    public ScoreLabel (CharacterSource generator, CharacterSource typist) {
        this.generator = generator;
        this.typist = typist;

        if (generator != null)
            generator.addCharacterListener(this);
        if (typist != null)
             typist.addCharacterListener(this);
    }

    public ScoreLabel () {
        this(null, null);
    }

    public synchronized void resetGenerator(CharacterSource newGenerator) {
        if (generator != null)
            generator.removeCharacterListener(this);
        generator = newGenerator;
        if (generator != null)
            generator.addCharacterListener(this);
    }

    public synchronized void resetTypist(CharacterSource newTypist) {
        if (typist != null)
            typist.removeCharacterListener(this);
        typist = newTypist;
        if (typist != null)
            typist.addCharacterListener(this);
    }

    public synchronized void resetScore() {
        score = 0;
        char2type = -1;
        setScore();
    }

    private void setScore() {
        // This method will be explained later in chapter 7
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                setText(Integer.toString(score));
            }
        });
    }

    private synchronized void newGeneratorCharacter(int c) {
        if (char2type != -1) {
            score--;
            setScore();
        }
        char2type = c;
    }

    private synchronized void newTypistCharacter(int c) {
        if (char2type != c) {
            score--;
        } else {
            score++;
            char2type = -1;
        }
        setScore();
    }

    public synchronized void newCharacter(CharacterEvent ce) {
        // Previous character not typed correctly - 1 point penalty
        if (ce.source == generator) {
	    newGeneratorCharacter(ce.character);
        }

        // If character is extraneous - 1 point penalty
        // If character does not match - 1 point penalty
        else {
	    newTypistCharacter(ce.character);
        }
    }
}

synchronized锁定是可重入的,即当前声明 synchronized的方法中调用此类的其他 synchronized方法时可以直接进入,无需再次获取锁操作。

public int getHoldCount()

查询当前线程保持此锁的次数。

对于与解除锁操作不匹配的每个锁操作,线程都会保持一个锁。

保持计数信息通常只用于测试和调试。例如,如果不应该使用已经保持的锁进入代码的某一部分,则可以声明如下:

class X {
   ReentrantLock lock = new ReentrantLock();
   // ...
   public void m() {
     assert lock.getHoldCount() == 0;
     lock.lock();
     try {
       // ... method body
     } finally {
       lock.unlock();
     }
   }
}

返回:

当前线程保持此锁的次数,如果此锁未被当前线程保持过,则返回 0

十、死锁

死锁会发生在两个或者以上的thread在等待两个或两个以上的lock被释放,且程序的环境却让lock永远无法释放。

十一、Lock公平(Fairness)

使用明确的lock时lock应该如何被授予?

1. 让lock应该以先到先服务的原则被授予。

2. 让它以能够服务最多请求的顺序来被授予。

3. 锁应该对系统最有利的形式来呗授予,无论它用于什么。(synchronized接近这种)

Java 线程第三版 第三章数据同步 读书笔记,布布扣,bubuko.com

时间: 2024-10-19 11:37:34

Java 线程第三版 第三章数据同步 读书笔记的相关文章

Java 线程第三版 第四章 Thread Notification 读书笔记

一.等待与通知 public final void wait() throws InterruptedException 等待条件的发生. public final void wait(long timeout) throws InterruptedException 等待条件的发生.如果通知没有在timeout指定的时间内发生,它还是会返回. public final void wait(long timeout, int nanos) throws InterruptedException

Java 螺纹第三版 第三章数据同步 读书笔记

多线程间共享数据问题 一.Synchronizedkeyword atomic一词与"原子"无关,它以前被觉得是物质的最小的单元,不能再被拆解成更小的部分. 当一个方法被声明成synchronized,要执行此方法的thread必须先取得一个token,我们将它称为锁. 一旦该方法取得(或者说是获得)锁,它将执行此方法然后释放掉(或者返回)此锁.无论方法时如何返回的(包含通过异常)该锁会被释放. 二.Volatilekeyword 假设变量被标示为volatile.每次使用该变量时都必

Java 线程第三版 第六章 高级同步议题 读书笔记

多线程数据同步错误比较难检测,因为通常是与事件的特定发生顺序有关. 一.同步术语 Barrier(屏障) barrier是多个Thread的集合点:所有的Thread都应该到齐在这个barrier之后才能允许它们继续下去. Condition variable(条件变量) 实际上不是变量,而是与某个lock有关联的变量. Event variable(事件变量) 条件变量的另一个名称. Critical section(临界区) 临界区是synchronized方法或者block. Lock(锁

《Java并发编程实战》第六章 任务运行 读书笔记

一. 在线程中运行任务 无限制创建线程的不足 .线程生命周期的开销很高 .资源消耗 .稳定性 二.Executor框架 Executor基于生产者-消费者模式,提交任务的操作相当于生产者.运行任务的线程则相当于消费者. 1. Executors 返回 ExecutorService 2. ExecutorService方法submit.execute 3. ExecutorService.submit 返回 Future 线程池,Executors方法介绍 方法名 解释 newFixedThre

Java 线程第三版 第五章 极简同步技巧 读书笔记

一.能避免同步吗? 取得锁会因为以下原因导致成本很高: 取得由竞争的锁需要在虚拟机的层面上运行更多的程序代码. 要取得有竞争锁的线程总是必须等到锁被释放后. 1. 寄存器的效应 计算机有一定数量的主寄存器用来存储与程序有关的数据. 从逻辑上的观点来看,每个Thread都有自己的一组寄存器.当操作系统将某个Thread分配给CPU时,它会把该Thread特有的信息加载到CPU的寄存器中.在分配不同的Thread给CPU之前,它会将寄存器的信息存下来.所以Thread间绝不会共享保存在寄存器的数据.

《DirectX 9.0 3D游戏开发编程基础》 第一章 初始化Direct3D 读书笔记

REF设备 参考光栅设备,他能以软件计算方式完全支持Direct3D Api.借助Ref设备,可以在代码中使用那些不为当前硬件所支持的特性,并对这此特性进行测试. D3DDEVTYPE 在程序代码中,HAL设备用值D3DDEVTYPE_HAL来表示.该值是一个枚举变量.REF设备用D3DDEVTYPE_REF来表示.这种类型非常重要,你需要铭记,因为在创建设备的时候,我们必须指定使用哪种设备类型. COM(组件对象模型) 创建COM接口时不可以使用c++关键字new.此外使用完接口,应调用Rel

《C++primer》v5 第2章 C++基础 读书笔记 习题答案

2.1 int,long long ,short 可表示范围和占用内存空间不同.具体与计算机有关. 无符号类型只能表示0和正数,带符号类型可以表示负数,0,正数. float是单精度,一般占用4个字节,double是双精度,一般占用8个字节,它们可表示的数据范围也不相同. 2.2 利率用double,本金和付款用int 2.3 unsigned u=10,u2=42; cout<<u2-u<<endl; cout<<u-u2<<endl; int i=10,

【转】apue《UNIX环境高级编程第三版》第一章答案详解

原文网址:http://blog.csdn.net/hubbybob1/article/details/40859835 大家好,从这周开始学习apue<UNIX环境高级编程第三版>,在此,我要感谢网易的一个工程师朋友和室友,没有他们,我不会开始真正的学习这本书,希望大家以后开始慢慢进步.废话少说,直接上课后习题了. UNIX高级编程第一章习题答案: 1.1在系统上验证,除根目录外,目录l和l l是不同的. 答:这个验证有很多方法可使用命令ls .cd.vim等,目录.指向当前目录,目录..指

王爽《汇编语言》第三版 第五章 [BX]和loop指令

5.1 [bx] mov ax,[bx]功能:bx 中存放的数据作为一个偏移地址EA ,段地址SA 默认在ds 中,将SA:EA处的数据送入ax中. 5.2 Loop指令 指令的格式是:loop 标号,CPU 执行loop指令的时候,要进行两步操作: 1.(cx)=(cx)-1: 2.判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行. 通常我们用loop指令来实现循环功能,cx 中存放循环次数. 5.3 在Debug中跟踪用loop指令实现的循环程序 5.4 Debug和汇编编译器