3.1 可见性

程序清单3-1 在没有同步的情况下共享变量(不要这么做)

/**
* 主线程和读线程都将访问共享变量:ready 和 number
* 结果可能
* 1、 主线程先运行完,读线程后运行:读线程在控制台打印输出42,
* 2、 读线程先执行完,主线程后执行:读线程在控制台打印输出0
* 3、 NoVisibility根本无法终止,读线程可能永远都看不到ready的值
* 4、 重排序(Reordering):读线程看到了主线程中顺序靠后写入的ready的值,但没看到主线程先写入的number的值
*/
@NotThreadSafe
public class NoVisibility {
private static boolean ready;
private static int number; //读线程
private static class ReaderThread extends Thread {
@Override
public void run() {
while(!ready)
Thread.yield();
System.out.println(number);
}
} public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}

程序清单3-2  非线程安全的可变整数类

//mutable 英[ˈmju:təbl] adj.    易变的,性情不定的;
@NotThreadSafe
public class MutableInteger { private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; }
}

程序清单3-3  线程安全的可变整数类

@ThreadSafe
public class SynchronizedInteger { @GuardedBy("this")private int value; public synchronized int getValue() { return value; } public synchronized void setValue(int value) { this.value = value; }
}

程序清单3-4 数绵羊

/**
* volatile通常用作某个操作完成、发生中断或者状态的标志
* volatile语义不足以确保递增操作(count++)操作的原子性
* asleep 状态量一旦在某个线程中被更改了,其它线程也应该看到
* */
@ThreadSafe
public class CountSheep { volatile boolean asleep; public void countSomeSheep() {
while(!asleep) {
for (int i = 0; i < 100; i++) {
System.out.println("sheep"+i);
}
}
}
}

3.2 发布与逸出

程序清单3-5   发布一个对象

/**
* “发布”一个对象的意思是:是对象能够在当前作用域之外的代码中使用。
* 1、将一个指向该对象的引用保存到其他代码可以访问的地方
* 2、在某一个非私有的方法中返回该引用
* 3、将引用传递到其他类的方法中
* @author guchunchao
*
*/
public class PublicObject { private static Set<Object> knownSecret; public void initialize() {
knownSecret = new HashSet<>(); //将方法体中new的对象发布到了方法体外的变量中
}
}

程序清单3-6  使内部的可变状态逸出(不要这么做)

/**
* 按如下方式发布states,任何修改者都能修改这个数组的内容,不安全
* @author gcc
*/
public class UnsafeStates {
private String[] states = {"aaa","bbb","ccc"}; public String[] getStates() { return states; }
}

程序清单3-7  隐式地使this引用逸出(不要这么做)

public class ThisEscape {
public ThisEscape(EventSource source) {
source.registerListener(
new EventListener() { //发布EventListener时,也隐含的将ThisEscape实例本身:this发布了
public void onEvent(Event e) {
doSomething(e);
}
}
);
}
}

程序清单3-8  使用工厂方法来防止this引用在构造过程中逸出

public class SafeListener {
private final EventListener listener;
private ThisEscape(EventSource source) {
listener = new EventListener(
public void onEvent(Event e) {
doSomething(e);
}
);
} public static SafeLisener newInstance(EventSource source) {
SafeLisener safe = new SafeLisener();
soure.registerListener(safe.listener);
} }

3.3  线程封闭

程序清单3-9 基本类型的局部变量与引用变量的线程封闭性

/**
* 栈封闭
* 由于任何方法都无法获得对基本类型的引用,因此基本类型的局部变量始终封闭在线程内
* @author guchunchao
*
*/
public class BasicTypeStackClose {
//candidate [ˈkændɪdət] 报考者; 申请求职者; 攻读学位者; 最后命运或结局如何已显然可见者;
public int loadTheArk(Collection<Object> candidates) {
SortedSet<Object> animals;
int numPairs = 0; //方法局部变量,始终在栈中
Object candidate = null; animals = new TreeSet<Object>();
animals.addAll(candidates); ++numPairs;
return numPairs; //即使被return了外界也无法修改。
}
}

程序清单3-10  使用ThreadLocal来维持线程封闭性

/**
* ThreadLocal对象通常用于防止对可变的、非线程安全的单实例变量(Singleon)或全局变量进行共享。
* ThreadLocal的get与set等访问接口或方法为每个使用该变量的线程都存有一份独立的副本,因此get总是能返回当前线程在调用set时设置的新值。
*
* 单线程应用程序中维持一个全局的数据库连接,并在程序启动时初始化,从而避免在调用每个方法时都要传递一个Connection对象。
*
* 而JDBC的连接对象不一定是线程安全的,因此当多线程应用程序在没有协同的情况下使用全局变量时,就不是线程安全的。
* 将JDBC连接保存到ThreadLocal对象中,每个线程都会拥有属于自己的JDBC连接。
*
* 更多ThreadLocal内容见:http://www.cnblogs.com/dolphin0520/p/3920407.html
*/
public class ThreadLocalTemplate {
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection("DB_URL"); //JDBC的Connection不一定是线程安全的,封装到ThreadLocal中保证每个线程都会拥有自己的JDBC连接对象副本
}
}; public static Connection getConnection() {
return connectionHolder.get();
}
}

3.4  不变性

不可变对象一定是线程安全的! Why?

程序清单3-11 在不可变对象基础上构建不可变类

/**
* final修饰的类:
* 从字面意思来理解就是不会发生变化的类,那么是什么不会发生变化呢,其实就是类的状态,也就是不变类的实例一旦被创建,其状态就不会发生变化。
* 举个例子:
* 如果人是一个class,那么我们中的每一个都是人这个类的具体的instance,
* 如果人这个类只有一个状态就是生身父母,那么它就是一个不变类,因为每一个人在出生的那一刹那,生身父母就已经被设置了值,而且终生都不会发生变化。
* 不变类有什么好处呢?
* 1) 不变类是线程安全的,由于不变类的状态在创建以后不再发生变化,所以它可以在线程之间共享,而不需要同步。
* 。。。。*/
@Immutable //[ɪˈmju:təbl]不可改变的;
public final class ThreeStooges { private final Set<String> stooges = new HashSet<>();//[stu:dʒ]喜剧里的配角或丑角,助手,伙伴; public ThreeStooges() {
stooges.add("hello");
stooges.add("world");
stooges.add("hello world");
} public boolean isStooge(String name) {
return stooges.contains(name);
}
}

程序清单3-12  对数值及因数分解结果进行缓存的不可变容器类

/**
* 对于在访问和更新多个相关变量是出现的竞态条件问题,可以通过将这些变量全部保存在一个不可变对象中来消除。
* 如果是一个可变的对象,那么就必须使用锁来确保原子性。
* 如果是一个不可变对象,那么当线程获得了对该对象的饮用后,就不必担心另一个线程会修改对象的状态。
* 如果要更新这些变量,可以创建一个新的容器对象,但其他使用原有对象的线程仍然会看到对象处于一致的状态。
* @author guchunchao
*
*/
@Immutable
public class OneValueCache {//存缓存的容器类
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
public OneValueCache(BigInteger lastNumber, BigInteger[] lastFactors) {
this.lastNumber = lastNumber;
this.lastFactors = lastFactors;
}
public BigInteger[] getLastFactors(BigInteger i) {
if(lastNumber == null || lastNumber.equals(i))
return null;
else
return Arrays.copyOf(lastFactors, lastFactors.length);
}
} //程序清单3-13 使用指向不可变容器对象的 volatile类型引用以缓存最新结果
@ThreadSafe
public class VolatileCacheFactorizer implements Servlet { private volatile OneValueCache cache = new OneValueCache(null, null); @Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
BigInteger i = extractFromReqest(req);
BigInteger[] factors = cache.getLastFactors(i);
if(factors == null) {
factors = factor(i);
cache = new OneValueCache(i, factors);
}
encodeIntoResponse(res,factors);
}
}

3.5 安全发布

程序清单3-14    在没有足够同步的情况下发布对象(不要这么做)

//不安全的发布
public Holder holder; public void initialize() {
holder = new Holder(42);
}

  

程序清单3-15 由于未被正确发布,因此这个类可能出现故障

//不安全的发布
public class Holder {
private int n;
public Holder(int n) {
this.n = n;
}
   public void assertSanity() {
if(n != n)
      throw new AssertionError("This Statement is false.");
   }
}

  

  

《JAVA并发编程实战》示例程序 第三章的更多相关文章

  1. java并发编程实战笔记---(第三章)对象的共享

    3.1 可见性 synchronized 不仅实现了原子性操作或者确定了临界区,而且确保内存可见性. *****必须在同步中才能保证:当一个线程修改了对象状态之后,另一个线程可以看到发生的状态变化. ...

  2. java并发编程实战:第十二章---并发程序的测试

    并发程序中潜在错误的发生并不具有确定性,而是随机的. 安全性测试:通常会采用测试不变性条件的形式,即判断某个类的行为是否与其规范保持一致 活跃性测试:进展测试和无进展测试两方面,这些都是很难量化的(性 ...

  3. 《Java并发编程实战》第十二章 测试并发程序 读书笔记

    并发测试分为两类:安全性测试(无论错误的行为不会发生)而活性测试(会发生). 安全測试 - 通常採用測试不变性条件的形式,即推断某个类的行为是否与其它规范保持一致. 活跃性測试 - 包含进展測试和无进 ...

  4. java并发编程实战:第十四章----构建自定义的同步工具

    一.状态依赖性管理 对于单线程程序,某个条件为假,那么这个条件将永远无法成真 在并发程序中,基于状态的条件可能会由于其他线程的操作而改变 可阻塞的状态依赖操作的结构 acquire lock on o ...

  5. 《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记

    一.锁的劣势 锁定后假设未释放.再次请求锁时会造成堵塞.多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销. 在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断. 锁可能 ...

  6. 《Java并发编程实战》第十六章 Java内存模型 读书笔记

    Java内存模型是保障多线程安全的根基,这里不过认识型的理解总结并未深入研究. 一.什么是内存模型,为什么须要它 Java内存模型(Java Memory Model)并发相关的安全公布,同步策略的规 ...

  7. java并发编程实战笔记---(第四章)对象的组合

    4.1设计线程安全的类 包含三个基本要素: 1.找出构成对象状态的所有变量 2.找出约束状态变量的不变性条件 2.简历对象状态的并发访问管理策略 对象的状态: 域 基本类型所有域, 引用类型包括被引用 ...

  8. java并发编程实战:第十六章----Java内存模型

    一.什么是内存模型,为什么要使用它 如果缺少同步,那么将会有许多因素使得线程无法立即甚至永远看到一个线程的操作结果 编译器把变量保存在本地寄存器而不是内存中 编译器中生成的指令顺序,可以与源代码中的顺 ...

  9. java并发编程实战:第十五章----原子变量与非阻塞机制

    非阻塞算法:使用底层的原子机器指令(例如比较并交换指令)代替锁来确保数据在并发访问中的一致性 应用于在操作系统和JVM中实现线程 / 进程调度机制.垃圾回收机制以及锁和其他并发数据结构 可伸缩性和活跃 ...

  10. 《Java并发编程实战》第十四章 构建自己定义的同步工具 读书笔记

    一.状态依赖性的管理 有界缓存实现的基类 @ ThreadSafe public abstract class BaseBoundedBuffer<E> { @GuardeBy( &quo ...

随机推荐

  1. Spring Boot使用Maven打包替换资源文件占位符

    在Spring Boot开发中,通过Maven构建项目依赖是一件比较舒心的事,可以为我们省去处理冲突等大部分问题,将更多的精力用于业务功能上.近期在项目中,由于项目集成了其他外部系统资源文件,需要根据 ...

  2. mssql sqlserver 分组排序函数row_number、rank、dense_rank用法简介及说明

    在实际的项目开发中,我们经常使用分组函数,对组内数据进行群组后,然后进行组内排序:如:1:取出一个客户一段时间内,最大订单数的行记录2: 取出一个客户一段时间内,最后一次销售记录的行记录——————— ...

  3. Linux(二)—— Unix&Linux 的基本概念

    Linux(二)-- Unix&Linux 的基本概念 计算机 = 主机(host)+ 终端(terminal) 主机 = 内核 + 实用工具 内核(kernel) 当计算机启动时,计算机要经 ...

  4. July 12th, 2018. Thursday, Week 28th.

    People love what other people are passionate about. 人总是会爱上别人倾注热情的事物. From La La Land. This quote has ...

  5. 51Nod 1004 n^n的末位数字

    思路:首先将0~9的平方的尾数放在一个数组a里面,方便后面直接调用,因为不论多大的数做什么运算,得到的结果的最后一位数只和运算前所有数的最后一位数有关系.新建变量d,z一个是底数,一个是幂次.循环判断 ...

  6. EM算法(Expectation Maximization)

    1 极大似然估计     假设有如图1的X所示的抽取的n个学生某门课程的成绩,又知学生的成绩符合高斯分布f(x|μ,σ2),求学生的成绩最符合哪种高斯分布,即μ和σ2最优值是什么? 图1 学生成绩的分 ...

  7. June. 25th 2018, Week 26th. Monday

    Change in all things is sweet. 有改变就会有美好. From Aristole. Change is always good, but embracing change ...

  8. WPF C# 命令的运行机制

    1.概述 1.1 WPF C# 命令的本质 命令是 WPF 中的输入机制,它提供的输入处理比设备输入具有更高的语义级别. 例如,在许多应用程序中都能找到的“复制”.“剪切”和“粘贴”操作就是命令. W ...

  9. Azure导出所有用户权限---powershell命令

      直接运行脚本         #requires -Version 3.0 -Modules AzureRM.Resourcesparam(    [switch]    $GroupRolesB ...

  10. Mistwald zoj 3497

    链接 [https://vjudge.net/contest/294259#problem/K] 题意 就是有个m*n矩阵 出发(1,1) 出口(m,n) 然后给出每个点能到大的四个位置 而且一旦到达 ...