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

一、能避免同步吗?

取得锁会因为以下原因导致成本很高:

取得由竞争的锁需要在虚拟机的层面上运行更多的程序代码。

要取得有竞争锁的线程总是必须等到锁被释放后。

1. 寄存器的效应

计算机有一定数量的主寄存器用来存储与程序有关的数据。

从逻辑上的观点来看,每个Thread都有自己的一组寄存器。当操作系统将某个Thread分配给CPU时,它会把该Thread特有的信息加载到CPU的寄存器中。在分配不同的Thread给CPU之前,它会将寄存器的信息存下来。所以Thread间绝不会共享保存在寄存器的数据。

当虚拟机进入synchronized方法或者块时,它必须重新加载本来已经缓存到自有寄存器上的数据。在虚拟机离开synchroized方法或者块之前,它必须把自有寄存器存入主寄存器中。

2. 重排语句的效应

在单独线程中,程序总是按照代码一行行的执行的,但是如果是多个线程并发,Java并不保证每个线程run方法的执行顺序,也就是可能其中一个线程执行到一半就会被暂时停止,执行其他线程,之后再切换回来。

多个线程间调度执行的无序性就是重排语句的效应。

3. 双重检查的Locking

二、Atomic变量

1. Atomic Class的概述

AtomicInteger, AtomicLong, AtomicBoolean, AtomicRefrences。Atomic的功能实现时通过使用use-level的Java程序无法访问的固有方法来完成的。

atomic package支持更复杂的变量类型吗?

一些不支持,例如字符或者浮点。

AtomicStampedReference能够让mark或stamp跟在任何对象的引用上。

AtomicMarkableReference提供一个包含对象引用结合boolean的数据结构。

2. 使用Atomic Class

import javax.swing.*;
import java.awt.event.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import javathreads.examples.ch05.*;

public class ScoreLabel extends JLabel implements CharacterListener {
    private AtomicInteger score = new AtomicInteger(0);
    private AtomicInteger char2type = new AtomicInteger(-1);
    private AtomicReference<CharacterSource> generator = null;
    private AtomicReference<CharacterSource> typist = null;

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

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

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

    public void resetGenerator(CharacterSource newGenerator) {
        CharacterSource oldGenerator;

        if (newGenerator != null)
            newGenerator.addCharacterListener(this);

        oldGenerator = generator.getAndSet(newGenerator);
        if (oldGenerator != null)
            oldGenerator.removeCharacterListener(this);
    }

    public void resetTypist(CharacterSource newTypist) {
        CharacterSource oldTypist;

        if (newTypist != null)
            newTypist.addCharacterListener(this);

        oldTypist = typist.getAndSet(newTypist);
        if (oldTypist != null)
            oldTypist.removeCharacterListener(this);
    }

    public void resetScore() {
        score.set(0);
        char2type.set(-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.get()));
            }
        });
    }

    public void newCharacter(CharacterEvent ce) {
        int oldChar2type;

        // Previous character not typed correctly - 1 point penalty
        if (ce.source == generator.get()) {
            oldChar2type = char2type.getAndSet(ce.character);

            if (oldChar2type != -1) {
                score.decrementAndGet();
                setScore();
            }
        }
        // If character is extraneous - 1 point penalty
        // If character does not match - 1 point penalty
        else if (ce.source == typist.get()) {
            while (true) {
                oldChar2type = char2type.get();

                if (oldChar2type != ce.character) {
                    score.decrementAndGet();
                    break;
                } else if (char2type.compareAndSet(oldChar2type, -1)) {
                    score.incrementAndGet();
                    break;
                }
            }

            setScore();
        }
    }
}

变量替换

score与char2type变量已经改成atomic变量。

以上resetScroe方法中对两个变量进行修改,虽然使用atomic能保证每个变量的原子性,但是如果多个线程同时执行resetScore方法依然会出现竞态条件。

有可能一个线程执行resetScore的第一行代码score.set(0);还未执行第二行,而另外一个线程可能已经执行newCharacter方法获取char2type的值,但是之前的线程执行resetScore方法还未对char2type进行修改。因为resetScore整个方法并不是原子的。

变更算法

resetGenerator与resetTypist两个方法,以resetGenerator方法为例,将generator变量变成AtomicRerence同样引发上面描述的问题。

通知与Atomic变量

import java.awt.*;
import javax.swing.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import javathreads.examples.ch05.*;

public class AnimatedCharacterDisplayCanvas extends CharacterDisplayCanvas implements CharacterListener, Runnable {

    private AtomicBoolean done = new AtomicBoolean(true);
    private AtomicInteger curX = new AtomicInteger(0);
    private AtomicInteger tempChar = new AtomicInteger(0);
    private Thread timer = null;

    public AnimatedCharacterDisplayCanvas() {
        startAnimationThread();
    }

    public AnimatedCharacterDisplayCanvas(CharacterSource cs) {
        super(cs);
        startAnimationThread();
    }

    private void startAnimationThread() {
        if (timer == null) {
            timer = new Thread(this);
            timer.start();
        }
    }

    public void newCharacter(CharacterEvent ce) {
        curX.set(0);
        tempChar.set(ce.character);
        repaint();
    }

    protected void paintComponent(Graphics gc) {
        char[] localTmpChar = new char[1];
        localTmpChar[0] = (char) tempChar.get();
        int localCurX = curX.get();

        Dimension d = getSize();
        int charWidth = fm.charWidth(localTmpChar[0]);
        gc.clearRect(0, 0, d.width, d.height);
        if (localTmpChar[0] == 0)
            return;

        gc.drawChars(localTmpChar, 0, 1,
                     localCurX, fontHeight);
        curX.getAndIncrement();
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
                if (!done.get()) {
                    repaint();
                }
            } catch (InterruptedException ie) {
                return;
            }
        }
    }

    public void setDone(boolean b) {
        done.set(b);
    }
}

使用Atomic变量的总结

乐观同步

程序代码抓住保护变量的值并作出此一瞬间没有其他修改的假设,然后程序代码就计算出该变量的新值并尝试更新该变量。

如果有其他的Thread同时修改了变量,这个更新就失败且程序必须重新执行这些步骤(使用变量的最新修改过的值)。

数据交换: getAndSet()

复杂的数据交换: get()与compareAndSet()

高级的atomic数据类型

import java.lang.*;
import java.util.concurrent.atomic.*;

public class AtomicDouble extends Number {
    private AtomicReference<Double> value;

    public AtomicDouble() {
        this(0.0);
    }

    public AtomicDouble(double initVal) {
        value = new AtomicReference<Double>(new Double(initVal));
    }

    public double get() {
        return value.get().doubleValue();
    }

    public void set(double newVal) {
        value.set(new Double(newVal));
    }

    public boolean compareAndSet(double expect, double update) {
        Double origVal, newVal;

        newVal = new Double(update);
        while (true) {
            origVal = value.get();

            if (Double.compare(origVal.doubleValue(), expect) == 0) {
                if (value.compareAndSet(origVal, newVal))
                    return true;
            } else {
                return false;
            }
        }
    }

    public boolean weakCompareAndSet(double expect, double update) {
        return compareAndSet(expect, update);
    }

    public double getAndSet(double setVal) {
        Double origVal, newVal;

        newVal = new Double(setVal);
        while (true) {
            origVal = value.get();

            if (value.compareAndSet(origVal, newVal))
                return origVal.doubleValue();
        }
    }

    public double getAndAdd(double delta) {
        Double origVal, newVal;

        while (true) {
            origVal = value.get();
            newVal = new Double(origVal.doubleValue() + delta);
            if (value.compareAndSet(origVal, newVal))
                return origVal.doubleValue();
        }
    }

    public double addAndGet(double delta) {
        Double origVal, newVal;

        while (true) {
            origVal = value.get();
            newVal = new Double(origVal.doubleValue() + delta);
            if (value.compareAndSet(origVal, newVal))
                return newVal.doubleValue();
        }
    }

    public double getAndIncrement() {
        return getAndAdd((double) 1.0);
    }

    public double getAndDecrement() {
        return getAndAdd((double) -1.0);
    }

    public double incrementAndGet() {
        return addAndGet((double) 1.0);
    }

    public double decrementAndGet() {
        return addAndGet((double) -1.0);
    }

    public double getAndMultiply(double multiple) {
        Double origVal, newVal;

        while (true) {
            origVal = value.get();
            newVal = new Double(origVal.doubleValue() * multiple);
            if (value.compareAndSet(origVal, newVal))
                return origVal.doubleValue();
        }
    }

    public double multiplyAndGet(double multiple) {
        Double origVal, newVal;

        while (true) {
            origVal = value.get();
            newVal = new Double(origVal.doubleValue() * multiple);
            if (value.compareAndSet(origVal, newVal))
                return newVal.doubleValue();
        }
    }

    public int intValue() {
        return DoubleValue().intValue();
    }

    public long longValue() {
        return DoubleValue().longValue();
    }

    public float floatValue() {
        return DoubleValue().floatValue();
    }

    public double doubleValue() {
        return DoubleValue().doubleValue();
    }

    public byte byteValue() {
        return (byte)intValue();
    }

    public short shortValue() {
        return (short)intValue();
    }

    public Double DoubleValue() {
        return value.get();
    }

    public boolean isNaN() {
        return DoubleValue().isNaN();
    }

    public boolean isInfinite() {
        return DoubleValue().isInfinite();
    }

    public String toString() {
        return DoubleValue().toString();
    }

    public int hashCode() {
        return DoubleValue().hashCode();
    }

    public boolean equals(Object obj) {
        Double origVal = DoubleValue();

        return ((obj instanceof Double)
                && (origVal.equals((Double)obj)))
               ||
               ((obj instanceof AtomicDouble)
                &&
(origVal.equals(((AtomicDouble)obj).DoubleValue())));
    }

    public int compareTo(Double aValue) {
        return Double.compare(doubleValue(), aValue.doubleValue());
    }

    public int compareTo(AtomicDouble aValue) {
        return Double.compare(doubleValue(), aValue.doubleValue());
    }
}

大量数据的更改

用两个变量的原子类:分数与字母变量。

import java.util.concurrent.atomic.*;

public class AtomicScoreAndCharacter {
    public class ScoreAndCharacter {
        private int score, char2type;

        public ScoreAndCharacter(int score, int char2type) {
            this.score = score;
            this.char2type = char2type;
        }

        public int getScore() {
            return score;
        }

        public int getCharacter() {
            return char2type;
        }
    }

    private AtomicReference<ScoreAndCharacter> value;

    public AtomicScoreAndCharacter() {
        this(0, -1);
    }

    public AtomicScoreAndCharacter(int initScore, int initChar) {
        value = new AtomicReference<ScoreAndCharacter>
                    (new ScoreAndCharacter(initScore, initChar));
    }

    public int getScore() {
        return value.get().getScore();
    }

    public int getCharacter() {
        return value.get().getCharacter();
    }

    public void set(int newScore, int newChar) {
        value.set(new ScoreAndCharacter(newScore, newChar));
    }

    public void setScore(int newScore) {
        ScoreAndCharacter origVal, newVal;

        while (true) {
            origVal = value.get();
            newVal = new ScoreAndCharacter
                         (newScore, origVal.getCharacter());
            if (value.compareAndSet(origVal, newVal)) break;
        }
    }

    public void setCharacter(int newCharacter) {
        ScoreAndCharacter origVal, newVal;

        while (true) {
            origVal = value.get();
            newVal = new ScoreAndCharacter
                         (origVal.getScore(), newCharacter);
            if (value.compareAndSet(origVal, newVal)) break;
        }
    }

    public void setCharacterUpdateScore(int newCharacter) {
        ScoreAndCharacter origVal, newVal;
        int score;

        while (true) {
            origVal = value.get();
            score = origVal.getScore();
            score = (origVal.getCharacter() == -1) ? score : score-1;

            newVal = new ScoreAndCharacter (score, newCharacter);
            if (value.compareAndSet(origVal, newVal)) break;
        }
    }

    public boolean processCharacter(int typedChar) {
        ScoreAndCharacter origVal, newVal;
        int origScore, origCharacter;
        boolean retValue;

        while (true) {
            origVal = value.get();
            origScore = origVal.getScore();
            origCharacter = origVal.getCharacter();

            if (typedChar == origCharacter) {
                origCharacter = -1;
                origScore++;
                retValue = true;
            } else {
                origScore--;
                retValue = false;
            }

            newVal = new ScoreAndCharacter(origScore, origCharacter);
            if (value.compareAndSet(origVal, newVal)) break;
        }
        return retValue;
    }
}

被封装的值是以只读的方式处理。set方法必须创建新的对象来封装被保存的新值。

三、Thread局部变量

import java.util.*;

public abstract class Calculator {

    private static ThreadLocal<HashMap> results = new ThreadLocal<HashMap>() {
        protected HashMap initialValue() {
            return new HashMap();
        }
    };

    public Object calculate(Object param) {
        HashMap hm = results.get();
	Object o = hm.get(param);
	if (o != null)
	    return o;
        o = doLocalCalculate(param);
        hm.put(param, o);
        return o;
    }

    protected abstract Object doLocalCalculate(Object param);
}

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

时间: 2024-08-06 03:46:27

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

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

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

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 线程第三版 第一章Thread导论、 第二章Thread的创建与管理读书笔记

第一章 Thread导论 为何要用Thread ? 非阻塞I/O I/O多路技术 轮询(polling) 信号 警告(Alarm)和定时器(Timer) 独立的任务(Task) 并行算法 第二章 Thread的创建与管理 一.什么是Thread ? Thread是所在主机执行的应用程序任务(task). 只有一个线程的例子: public class Factorial { public static void main(String[] args) { int n = 5; System.ou

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

多线程间共享数据问题 一.Synchronized关键字 atomic一词与"原子"无关,它曾经被认为是物质的最小的单元,不能再被拆解成更小的部分. 当一个方法被声明成synchronized,要执行此方法的thread必须先取得一个token,我们将它称为锁.一旦该方法取得(或者说是获得)锁,它将运行此方法然后释放掉(或者返回)此锁.不管方法时怎样返回的(包括通过异常)该锁会被释放. 二.Volatile关键字 如果变量被标示为volatile,每次使用该变量时都必须从主寄存器中读出

Java 线程第三版 第九章 Thread调度 读书笔记

一.Thread调度的概述 import java.util.*; import java.text.*; public class Task implements Runnable { long n; String id; private long fib(long n) { if (n == 0) return 0L; if (n == 1) return 1L; return fib(n - 1) + fib(n - 2); } public Task(long n, String id)

Java 线程第三版 第八章 Thread与Collection Class 读书笔记

JDK1.2引入最有争议性的改变是将集合类默认为不是Thread安全性的. 一.Collection Class的概述 1. 具有Threadsafe 的Collection Class: java.util.Vector(List) 列表集合,通过索引操作. java.util.Stack(List) 继承自Vector,提供LIFO的顺序操作push进入,pop出元素. java.util.Hashtable(Map) 一个简单.无序的key与value的映射. java.util.conc

《Java并发编程实战》第十一章 性能与可伸缩性 读书笔记

造成开销的操作包括: 1. 线程之间的协调(例如:锁.触发信号以及内存同步等) 2. 增加的上下文切换 3. 线程的创建和销毁 4. 线程的调度 一.对性能的思考 1 性能与可伸缩性 运行速度涉及以下两个指标: 某个指定的任务单元需要"多快"才能处理完成.计算资源一定的情况下,能完成"多少"工作. 可伸缩性: 当增加计算资源时(例如:CPU.内存.存储容器或I/O带宽),程序的吞吐量或者处理能力能相应地增加. 2 评估各种性能权衡因素 避免不成熟的优化.首先使程序正

王爽《汇编语言》第三版 第五章 [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和汇编编译器

光源 材质 简析——基于《real time rendering》第三版 第五章

对于真是世界的渲染,有三个重要的组成部分,光源,材质,以及摄像机.下面,我们一个一个来简单介绍一下. 光源:方向光,点光源,聚光灯.但是,在和物体表面交互的时候,光源对物体表面的影响是依赖光的辐照度(irradiance)EL的,也就是单位面积上通过光的能量,这个能量由受到光和表面的夹角以及光本身的能量来表示.通常我们用RGB向量三种颜色来表示不同颜色光的辐照度.那么,当光的方向和表面垂直时,单位面积上的辐照度时最大的.当光的方向和表面有夹角时,光的辐照度开始变弱.如下图 可以看出,光的密度一样