Java 线程第三版 第五章 极简同步技巧 读书笔记
来源:互联网 发布:知乎双刀周杰伦 编辑:程序博客网 时间:2024/05/16 01:56
一、能避免同步吗?
取得锁会因为以下原因导致成本很高:取得由竞争的锁需要在虚拟机的层面上运行更多的程序代码。
要取得有竞争锁的线程总是必须等到锁被释放后。
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);}
1 0
- Java 线程第三版 第五章 极简同步技巧 读书笔记
- Java 线程第三版 第三章数据同步 读书笔记
- Java 线程第三版 第六章 高级同步议题 读书笔记
- Java 线程第三版 第四章 Thread Notification 读书笔记
- Java 线程第三版 第九章 Thread调度 读书笔记
- 《Java并发编程实战》第五章 同步容器类 读书笔记
- Thinking in Java第三版读书笔记-第五章:隐藏实现
- 《CSS权威指南第三版》第五章的读书笔记
- 读书笔记-java网络编程-3线程-同步
- Java 线程第三版 第一章Thread导论、 第二章Thread的创建与管理读书笔记
- Java 线程第三版 第一章Thread导论、 第二章Thread的创建与管理读书笔记
- Java 线程第三版 第八章 Thread与Collection Class 读书笔记
- 读书笔记:Windows核心编程第八章---用户方式中的线程同步技巧
- 线程同步基础读书笔记
- 读书笔记-APUE第三版-(11)线程
- 计算机仿真中的HLA技术 (第五章读书笔记) 联邦同步
- 《深入理解Linux内核》--第五章 内核同步:读书笔记
- 第三章线程同步辅助类
- hdu 1285 确定比赛名次——拓扑排序
- poj 1611 The Suspects(并查集)
- SQL 如何表示引号
- 隐马模型基本要素及基本三问题
- Ubuntu安装后怎么以root角色登录
- Java 线程第三版 第五章 极简同步技巧 读书笔记
- 摊派风波 娃哈哈爱迪生奶粉内外之困:真正危机还在后面
- 算法
- 金蝶K3管理软件PDA条码解决方案,盘点机与金蝶K3无缝对接
- 插入排序
- cocos2d-x 3.0 final 移植 android
- Android实战技巧:ViewStub的应用
- QWT
- linux 计划任务定时发邮件 phpmailer 和 thinkphp