1、ThreadLocal

  Java中的ThreadLocal类可以让你创建的变量只被同一个线程进行读和写操作。因此,尽管有两个线程同时执行一段相同的代码,而且这段代码又有一个指向同一个ThreadLocal变量的引用,但是这两个线程依然不能看到彼此的ThreadLocal变量域。ThreadLocal类提供了get和set等访问接口,这些方法为每个线程都存有一个独立的副本,因此每次get总是返回当前线程上一次最小使用set设置的值。

private ThreadLocal myThreadLocal = new ThreadLocal();

  这样实例化一个ThreadLocal类,每个线程仅需要实例化一次即可。

myThreadLocal.set("A  local value");
String threadLocalValue = (String) myThreadLocal.get();

  这样使用set和get来存储和读取一个String对象,因为get返回的是一个Object对象,所以需要强制类型转换,set()方法则依赖一个Object对象参数。

  为了不使用强制类型转换可以使用泛型来实例化一个ThreadLocal对象。

private ThreadLocal myThreadLocal1 = new ThreadLocal<String>();

  这样读取的时候就不需要进行强制类型转换了

myThreadLocal1.set("Hello ThreadLocal");
String threadLocalValues = myThreadLocal.get();

  然后我们还可以可以通过ThreadLocal子类的实现,并覆写initialValue()方法,就可以为ThreadLocal对象指定一个初始化值。

private ThreadLocal myThreadLocal = new ThreadLocal<String>() {
@Override protected String initialValue() {
return "This is the initial value";
}
};

  这样指定的初始值对所有线程都是可见的,在set方法被调用之前所有线程读取到的都将是这个初始值。

  一个完整的ThreadLocal示例

class myThread implements Runnable{
private ThreadLocal myThreadLocal = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return "a Initialization value ";
}
}; @Override
public void run() {
System.out.println(myThreadLocal.get());
myThreadLocal.set("" + Math.random());
System.out.println(myThreadLocal.get());
}
} public class Fun { public static void main(String[] args) {
myThread myRunnabe = new myThread();
Thread myThread1 = new Thread(myRunnabe);
Thread myThread2 = new Thread(myRunnabe); myThread1.start();
myThread2.start(); }
}

  输出:

a Initialization value
a Initialization value
0.3672775208272421
0.5694837916871707

2、不变性

  如果某个对象在创建之后就不能被修改,那么这个对象就被称为不可变对象。因为其状态将不会被改变,所以不同线程访问该对象的时候是安全的

  满足以下条件的对象是不可变的:

  • 对象在创建以后其状态就不能被修改
  • 对象的所有域都是final类型的
  • 对象是正确创建的

  在可变对象基础上构建不可变对象

    public final class Aclass{
private final Set<String> set = new HashSet<String>();
public Aclass(){
set.add("aaa");
set.add("bbb");
set.add("ccc");
}
public boolean isSet(String word){
return set.contains(word);
}
}

  这样一个类是可变,它的set对象是可变的,但是这个对象在创建之后就是不可改变的了,对于final类型的set对象只能访问不能被修改。

3、安全发布

  在某些情况下我们希望在多个线程之间共享对象,此时必须保证安全的进行共享。

public Holder holder;
public void init(){
holder = new Holder(42);
}

  这个holder看起来是一个不可变对象,但其实它并不是,他并不满足“对象是正确创建的”这个条件,某些线程将会看到尚未创建完场的对象

  可变对象必须通过安全的方式来进行发布,这通常都意味着在发布和使用该对象的线程都必须使用同步。

  要安全得发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见,一个正确构造的对象可以通过以下方式来正确发布:

  • 在静态初始化函数中初始化一个对象引用
public static Holder holder = new Holder(42); 
  • 将对象的引用保存到volatile类型的域或者AtomicReferance对象中
  • 将对象的引用保存到某个正确构造对象的final类型中
  • 将对象得引用保存到一个由锁保护的域中

  在线程安全库中的容器提供以下的安全发布保证:

  • 通过将一个键或者值放入Hashtable、synchronizedMap或者ConcurrentMap中,可以安全地将它发布给任何从这些容器中访问它的线程(无论是直接访问还是通过迭代器访问)
  • 通过将某个元素放入Vector、CopyOnWriteArrayList、CopyOnWriteArraySet、synchronizedList或synchronizedSet中,可以将该元素安全地发布到任何从这些容器中访问该元素的线程
  • 通过将某个元素放入BlockingQueue或者ConcurrentLinkedQueue中,可以将该元素安全地发布到任何从这些队列中访问该元素的线程。

  事实不可变对象:如果对象从技术上来看是可变的,但其状态在发布以后就不会在改变,那么把这种对象称为“事实不可变对象”,刚刚的holder就是一个事实不可变对象。

  所以最后对象的发布需求取决于它的可变性:

  • 不可变对象可以通过任意机制发布
  • 事实不可变对象必须通过安全方向进行发布
  • 可变对象必须通过安全方式进行发布,并且必须是线程安全的或者由某个锁保护起来的

  在并发程序中使用和共享对象时,可以使用以下一些实用的策略:

  • 线程关闭:对象只能由一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改
  • 只读共享:任何线程都不能修改的对象。
  • 线程安全共享:在线程内部实现同步。
  • 保护对象:通过持有特定的锁才能访问

Java并发编程实战笔记—— 并发编程2的更多相关文章

  1. Java并发编程实战笔记—— 并发编程1

    1.如何创建并运行java线程 创建一个线程可以继承java的Thread类,或者实现Runnabe接口. public class thread { static class MyThread1 e ...

  2. Java并发编程实战笔记—— 并发编程4

    1.同步容器类 同步容器类都是线程安全的,但在某些情况下可能需要额外的客户端加锁保护复合操作. 容器上常见的复合操作包括但不限于:迭代(反复访问数据,直到遍历完容器中所有的元素为止).跳转(根据指定顺 ...

  3. Java并发编程实战笔记—— 并发编程3

    1.实例封闭 class personset{ private final Set<Person> myset = new HashSet<Person>(); public ...

  4. Java并发编程实战.笔记十一(非阻塞同步机制)

    关于非阻塞算法CAS. 比较并交换CAS:CAS包含了3个操作数---需要读写的内存位置V,进行比较的值A和拟写入的新值B.当且仅当V的值等于A时,CAS才会通过原子的方式用新值B来更新V的值,否则不 ...

  5. Java并发编程实战 01并发编程的Bug源头

    摘要 编写正确的并发程序对我来说是一件极其困难的事情,由于知识不足,只知道synchronized这个修饰符进行同步. 本文为学习极客时间:Java并发编程实战 01的总结,文章取图也是来自于该文章 ...

  6. 【ARM-Linux开发】OpenACC并行编程实战笔记

    今年运气比较好,学了cuda之后,了解到了gpu的另两种使用语言opencl和openacc,  opencl(Open Computing Language ,开放计算语言)是面向异构系统的并行编程 ...

  7. 多线程-java并发编程实战笔记

    线程安全性 编写线程安全的代码实质上就是管理对状态的访问,而且通常都是共享的,可变的状态. 一个对象的状态就是他的数据,存储在状态变量中,比如实例域或静态域.所谓共享是指一个对象可以被多个线程访问:所 ...

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

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

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

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

随机推荐

  1. Oracle数据库---包

    --根据员工号或员工姓名获取员工的信息--根据员工号或员工姓名删除员工的信息--创建包规范CREATE OR REPLACE PACKAGE overload_pkgIS FUNCTION get_i ...

  2. VMware下的Centos7实践Kvm虚拟化(通俗易懂)

    虽然网上已经有很多关于kvm安装的教程了,但我还是看得头晕,有的教程里安装的包很多,有的很少,也没说明那些安装包的作用是干嘛的,用的命令也不一样,也没解释命令的意思是什么. 我重新写一个教程,尽量通俗 ...

  3. 学Redis这篇就够了

    Redis 简介 Redis 优势 Redis 数据类型 string hash list set Zset 小总结 基本命令 发布订阅 简介 实例 发布订阅常用命令 事务 实例 Redis 事务命令 ...

  4. C语言学习书籍推荐《C语言入门经典(第5版)》下载

    霍尔顿 (Ivor Horton) (作者), 杨浩 (译者) 下载地址:点我 C语言是每一位程序员都应该掌握的基础语言.C语言是微软.NET编程中使用的C#语言的基础:C语言是iPhone.iPad ...

  5. 查询亿级数据毫秒级返回!Elasticsearch 是如何做到的?

    掌握搜索技能,才能在庞大的数据集中找到准确的目标.本篇就带你进入另一个非凡的旅程,即使你没有像Google或Baidu一样强大的技术,一样也可以做出与之相匹敌的用户体验. 搜索是现代软件必备的一项基础 ...

  6. ElasticStack学习(九):深入ElasticSearch搜索之词项、全文本、结构化搜索及相关性算分

    一.基于词项与全文的搜索 1.词项 Term(词项)是表达语意的最小单位,搜索和利用统计语言模型进行自然语言处理都需要处理Term. Term的使用说明: 1)Term Level Query:Ter ...

  7. 用框架名唬人谁都会,那你知道Web开发模式吗?——莫问前程莫装逼

    前言:这两天总结了一些Servlet和JSP里面的知识,写了几篇博客,果然有种“温故而知新”的感觉,学完这些,继续前行,开始整合框架里的知识,框架虽好,可底层原理该掌握的也得掌握,防止以后做项目的时候 ...

  8. WGS84坐标与web墨卡托投影坐标转换

    许久没有使用坐标转换,记忆有些模糊了,以后还是会用到,先将WGS84与web墨卡托转换复习一下: 1.84转web墨卡托 //核心公式 平面坐标x = 经度*20037508.34/108 平面坐标y ...

  9. Android生成随机数

    此方法通过把当前时刻长整型数传给Random对象,让它产生的值随着时间而变化. String s = ""; Random ran =new Random(System.curre ...

  10. storm入门demo

    一.storm入门demo的介绍 storm的入门helloworld有2种方式,一种是本地的,另一种是远程. 本地实现: 本地写好demo之后,不用搭建storm集群,下载storm的相关jar包即 ...