`
wanxiaotao12
  • 浏览: 473643 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java并发编程实战-学习笔记

 
阅读更多

第3章 对象的共享

3.1、可见性

可见性概念:在多个线程对一个变更操作时, 其中一个线程将变量修改后,其他线程在读取该变量的时候,得到的是修改后的值(即最新的值)。因此为确保多个线程之间对内存写入操作的可见性,必须使用同步机制。

 

3.1.1、失效数据

在多线程中,当没有同步机制时,某一线程取的数据可能是失效的数据(即该线程先取出变量,但后来被其他线程修改了,但是该线程没有被通知,所以该线程取的变量的数据是失效的)

 

3.1.2、32位基本基本类型数据的读取操作是线程安全的,但是非volatile的64位操作(如long、double类型数据变量)不是线程安全的,这类数据线程安全的方法:使用volatile关键字,或者使用锁

 

3.1.3、内置锁可以保证数据可见性

加锁的含义不仅局限于互斥行为, 还包括内存可见性。为确保所有线程都能看到共享变量的最新值,所有执行读取操作或者写操作的线程必须在同一个锁上同步。

 

3.1.4、Volatile变量

稍弱的同步机制,用来确保变量的更新操作通知到其他线程。比sychronized关键字更轻量级的同步机制。

volatile的使用条件(满足下面所有条件):

a、对变量的写操作不依赖于变量的当前值(如a++,a=a+10),才能使用volatile变量

b、该变量不会与其他状态变量一起纳入不变性条件

c、在访问变量时不需要加锁

 

3.2 发布与逸出

发布一个对象:使对象能够在当前作用域之外的代码中使用。如,将一个指向该对象的引用保存到其他代码可以访问的地方,或者在某一个非私有的方法中返回该引用,或者将引用传递到其他类的方法中。

在多数情况下, 确保对象及内部状态不被发布。发布内部状态可能会破坏封装性, 并使得程序难以维持不变性条件。如,如果在对象构造完成之前就发布对象,就会破坏线程安全性。

逸出:当某个不应该发布的对象被发布时,这种情况被称为逸出。如:将对象的引用保存到一个公有的静态变量中, 则其他任何类和线程都可以看到该对象,所以不利于统一做同步的控制。

安全的对象构造过程:不要在构造过程中使用this引用逸出。在构造函数中创建线程,this引用都会被新创建的线程共享,所以在构造函数时, 不要启动线程,而是通过start或initialize方法来启动。

 

3.3 线程封闭

在单线程中,不需要线程同步,这种技术称为线程封闭,实现线程安全最简单的方式之一。当某个对象封闭在一个线程中时, 这个用法将自动实现线程安全性,即使被封闭的对象本身不是线程安全的。

 

3.3.1 Ad-hoc 线程封闭:完全由程序来实现,由于这种技术的脆弱性,程序中尽量少用。(不太理解这种技术)

3.3.2 栈封闭:只能通过局部变量才能访问对象。当执行某一方法时,JVM会给每个线程都分配各自的栈,所以方法中的局部变量是封闭的。

对于基本类型的局部变量,因为任何方法都无法获得对基本类型的引用,所以不会破坏栈封闭性。

对于对象引用对象的栈封闭性,程序员需要多做一些工作以确保被引用的对象不会逸出

 

3.3.3 ThreadLocal类

维持线程封闭性的一种更规范方法是使用ThreadLocal,这个类能使线程中的某个值与保存值的对象关联起来。ThreadLocal有get,set方法,这些方法为每一个线程创建独立的副本,因此get总是返回由当前执行线程在调用set时设置的最新值。

ThreadLocal可以避免在调用每个方法时都要传递该ThreadLocal类变量。

缺点:ThreadLocal变量类似于全局变量,它能降低代码的可重用性,并在类之间引入隐含的耦合性,因此使用要小心。

 

3.4 不变性

对象的状态(成员变量)都是不可变的

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

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

a、对象创建以后其状态变不能修改

b、对象的所有域都是final类型

c、对象是正确创建的(在对象的创建期间,this没有逸出)

 

安全共享对象的策略:

a、线程封闭

b、只读共享

c、线程安全共享:线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有接口来进行访问而不需要进一步的同步。

e、保护对象:被保护的对象只能通过持有锁来访问

 

 

第四章 对象的组合

4.1 设计线程安全类

通过封闭技术,可以使得在不对整个程序进行分析的情况下就可以判断一个类是否是线程安全的。

设计过程,包含三个基本要素:

a、找出构成对象状态的所有变量

b、找出约束状态变量的不变性条件

c、建立对象状态的并发访问管理策略

 

分析对象的状态,首先从对象的域开始。如果全是基本数据类型,那么些域构成对象的全部状态。如果是引用其他对象的域,则该对象的状态包含引用对象的域。

 

4.1.1 收集同步需求

4.1.2 依赖状态的操作

4.1.3 状态的所有权:即成员变量所属的类

 

4.2 实例封闭

将数据封装在对象内部(私有成员变量),将数据访问限制在对象的方法上,从而更容易使用锁保证同步

通过封闭机制确保线程安全:

public class PersionSet{

private final Set<Person> mySet = new HashSet<Person>();

public synchronized void addPerson(Person p){

mySet.add(p);

}

 

public synchronized boolean containsPerson(Person p){

return mySet.contains(p);

}

}

 

本实例将HashSet不是线程安全的类,使用内置锁封闭成线程安全的类。但没有考虑Person类的线程安全,如果Person类是可变的,那么在访问从PersonSet中获得Person对象时, 还要额外的同步。

 

4.2.1 java监视器模式,委托机制(使用synchronized内置锁,将不安全的类进行封装,变为线程安全的类,这就是线程安全的委托,将不安全的委托给线程安全的类)

 

4.4 在现有的线程安全类中添加功能

1、不要用子类继承安全类,如果父类的同步策略改变了,则子类线程安全被破坏

2、将安全类做为成员变量,创建一个新的类,在新类中加锁

public class ListHelper<E>{

public List<E> list = Collections.synchronizedList(new ArrayList<E>);

//没有则添加

public synchronized boolean putIfAbsent(E x) {

boolean absent = !list.contains(x);

if(absent) {

list.add(x);

}

 

return ablsent;

}

}

 

不要用上述的方法,上面的表面是使用锁, 但是使用了不同的锁, 所以不能保证同步

 

正确的方式:(在客户端加锁机制)

public class ListHelper<E>{

public List<E> list = Collections.synchronizedList(new ArrayList<E>);

//没有则添加

public void boolean putIfAbsent(E x) {

synchronized(list) {

boolean absent = !list.contains(x);

if(absent) {

list.add(x);

}

 

return ablsent;

}

}

}

 

组合方式:

public class ImprovedList<T> implements List<T>{

private final List<T> list;

public ImprovedList(List<T> list){

this.list = list;

}

//没有则添加

public synchronized boolean putIfAbsent(E x) {

boolean absent = !list.contains(x);

if(absent) {

list.add(x);

}

 

return ablsent;

}

 

public synchronized void clear(){

list.clear();

}

//按照这种方式委托List的其他方法

}

 

第5章 基础构建模块

5.1同步窗口类 Vector HashTable

5.1.1 同步容器类的问题

同步容器类都是线程安全的,但在某些情况下需要额外的客户端加锁来保护复合操作。

如:Vector中定义的两个方法

public static Object getLast(Vector list) {

int lastIndex = list.size()-1;

return list.get(lastIndex);

}

 

public static void deleteLast(Vector list){

int lastIndex = list.size()-1;

list.remove(lastIndex)

}

线程A查找最后一个元素getLast(),线程B删除最后一元素,当A执行完int lastIndex = list.size()-1;时,B将最后一元素删除,这时A将找到最后个元素!

解决方法:在客户端加锁。

public static Object getLast(Vector list) {

synchronized(list){

int lastIndex = list.size()-1;

return list.get(lastIndex);

}

}

 

public static void deleteLast(Vector list){

sychronized(list){

int lastIndex = list.size()-1;

list.remove(lastIndex)

}

}

5.2 并发容器  

并发容器来代替同步容器,可以极大地提高伸缩性并降低风险。

ConcurrentHashMap(用于代替HashTable)、Queue、BlockingQueue

Queue用来临时保存一组待处理的元素,非阻塞的,如果没有元素,则返回空值

它有几种实现:ConcurrentLinkedQueue(这是传统的先进先出队列)

PriorityQueue(非并发的优先队列)

 

BlockingQueue:扩展了Queue,增加了可阻塞的插入和获取等操作。如果队列为空,那么获取元素的操作将一直阻塞,直到队列中出现一个可用的元素。在生产者,消费者这种设计模式中, 阻塞队列是非常有用的。

 

5.2.1 ConcurrentHashMap

ConcurrentHashMap并不是将每个方法都在同一个锁上同步并使得每次只能有一个线程访问容器,而是使用一种粒度更细的加锁机制来实现更大程度的共享,这种机制称为分段锁。ConcurrentHashMap带来的结果是:在并发访问环境下将实现更高的吞吐量,而在单线程环境中只损失非常小的性能。

 

不需要在迭代器上加锁

只有当程序需要加锁Map以进行独占时, 这个时候不能使用ConcurrentHashMap,其他时候都可以使用

 

5.2.3 CopyOnWriteArrayList 替代同步List,  CopyOnWriteArraySet:替代同步Set

 

5.3 阻塞队列和生产者-消费者模式

BlockingQueue有多种实现:LinkedBlockingQueue、ArrayBlockingQueue 都是FIFO队列,二者分别与LinkedList、ArrayList类似

PriorityBlockingQueue是一个按优先级排序的队列,当你希望按照某种顺序而不是FIFO来处理元素时,使用这个队列

SynchronousQueue:实际上不是真正的队列,因为他不会为队列中元素维护存储空间。生产者生产的产品, 可以直接交给消费者来消费,减少时间的延迟。

take、put方法是阻塞的方法

阻塞队列存放任务, 供生产者、消费者使用,同时可以有多个生产者(即多线程)、多个消费者。在执行的过程中, 使用阻塞队列的使用情况,来调整生产者、消费者间的比例

 

在构建高可靠的应用程序时,有界队列是一种强大的资源管理工具:他们抑制并防止产生过多的工作项,使应用程序在负荷过载的情况下变得更加健壮。

 

5.4 阻塞方法与中断方法

 

5.5 同步工具类

5.5.1 闭锁

相当于一扇门,在闭锁到达结束状态前,这扇门一直是关闭的,闭锁可以用来确保某些活动直到其他活动都完成后才继续执行。

CountDownLatch是一种闭锁的实现,可以使一个或多个线程等待一组事件发生。闭锁状态包括一个计数器,该计数器被初始化为一个正数,表示需要等待的事件数量。countDown方法递减计数器,表示有一个事件已经发生了, 而await方法等待计数器达到0,这表示所有需要等待的事件都已经发生。如果计数器非0,那么await会一直阻塞到计数器为0,或者等待中的线程中断,或者等待超时。

 

5.5.2 FutureTask

 

分享到:
评论
2 楼 wanxiaotao12 2012-12-10  
aswang 写道
总结的不错,
正在读《java并发编程实战》,看到其中的不变性没有给出明确的定义,想到网上搜搜看,结果找到你的文章,总结的很不错!

理解一些概念很重要,理解概念是第一步!
1 楼 aswang 2012-12-09  
总结的不错,
正在读《java并发编程实战》,看到其中的不变性没有给出明确的定义,想到网上搜搜看,结果找到你的文章,总结的很不错!

相关推荐

    Java并发编程学习笔记.rar

    这本"Java并发编程学习笔记"可能是作者在深入研究Java并发特性、工具和最佳实践过程中积累的心得体会。下面,我们将根据这个主题,探讨一些关键的Java并发编程知识点。 1. **线程与进程**:在多任务环境中,线程是...

    Java并发实践-学习笔记

    这份"Java并发实践-学习笔记"涵盖了这个关键主题,旨在帮助开发者深入理解和掌握Java中的并发机制。以下是对这份笔记可能包含的一些核心知识点的详细阐述: 1. **Java并发基础**:首先,笔记可能会介绍Java并发的...

    java并发编程实践笔记

    ### Java并发编程实践笔记知识点详解 #### 一、保证线程安全的方法 1. **不要跨线程访问共享变量:** 当多个线程共享某个变量时,若其中一个线程修改了该变量,其他线程若没有正确同步,则可能读取到错误的数据。...

    Java并发编程实战(中文版).rar

    《Java并发编程实战》是一本深入探讨Java多线程和并发编程的经典著作,它为开发者提供了全面、实用的指导,帮助他们理解和解决并发问题。这本书的中文版使得更多的中国开发者能够受益于其丰富的知识和实践经验。 ...

    java7源码-Concurryency-Learning:并发编程学习笔记

    Java并发编程学习笔记 本项目整理自《Java7并发编程实战手册》,感兴趣的话推荐阅读原著 本章内容包括: 线程的创建和运行 线程信息的获取和设置 线程的中断 线程中断的控制 线程的Hibernate和恢复 等待线程的终止 ...

    java笔记 Java-Web笔记 J2EE三大框架笔记

    多线程和IO流则涉及并发编程和数据读写,对于高性能的Web应用至关重要。 其次,Java-Web笔记进一步讲解了Servlet、JSP、过滤器、监听器等Java Web开发的核心技术。Servlet是Java Web应用的基础,用于接收和响应HTTP...

    《java学习》-SSM实战项目-Java高并发秒杀API,详细流程+学习笔记.zip

    在Java学习的过程中,SSM(Spring、SpringMVC、...通过这个SSM实战项目,开发者可以实践以上知识点,加深对Java高并发编程的理解,提高解决问题的能力。同时,结合学习笔记,有助于巩固理论知识,形成完整的知识体系。

    Java高并发视频教学,并带实战java高并发程序设计,高并发面试题目

    "java高并发.txt"可能是一份文档或笔记,涵盖了Java并发编程的核心概念和技术。它可能详细解释了线程的生命周期、线程安全问题(如数据竞争、活锁、死锁)、并发工具类(如CountDownLatch、CyclicBarrier、Semaphore...

    并发编程之一 日常学习笔记

    综上所述,这一系列学习笔记涵盖了并发编程的关键概念和实战技巧,包括Java内存模型、线程池、并发容器的使用以及常见数据结构的线程安全问题。通过深入学习这些内容,开发者可以更好地理解和解决多线程环境下的编程...

    Java项目学习笔记: SSM实战项目-Java高并发秒杀API,详细流程+学习笔记

    在本Java项目学习笔记中,我们关注的是SSM(Spring、SpringMVC、MyBatis)框架下的高并发秒杀API实现。这是一个典型的电商场景,其中涉及到的技术点广泛且实用,对于提升Java开发者处理高并发问题的能力至关重要。...

    JUC并发编程学习笔记(硅谷)

    Java并发编程是Java开发中的重要领域,特别是在大型分布式系统或者高并发应用中,对线程安全和性能优化的理解与实践至关重要。"JUC并发编程学习笔记(硅谷)"很可能包含了关于Java并发工具集(Java Util Concurrency, ...

    网络编程-狂神说Java学习笔记

    "狂神说Java学习笔记"提供了深入理解和实践网络编程的宝贵资源。本笔记主要涵盖了以下几个关键知识点: 1. **Java网络编程基础**:Java通过Socket API提供了网络通信的能力。Socket代表了网络上的两个应用程序之间...

    《java学习》-java后端学习笔记.zip

    这份《java学习》-java后端学习笔记.zip压缩包包含了多个关键主题,旨在帮助初学者和有经验的开发者深入理解Java编程语言及其在后端应用中的实践。以下是这些主题的详细解析: 1. Git&GitHub.pdf:版本控制系统Git...

    《IT学习资料》-SSM实战项目-Java高并发秒杀API,详细流程+学习笔记.zip

    《IT学习资料》-SSM实战项目-Java高并发秒杀API是一份全面的教程,旨在帮助学习者掌握如何在Java环境下使用Spring、SpringMVC和MyBatis(简称SSM)框架构建高并发的秒杀系统。这个项目涵盖了从项目初始化到实现完整...

    Java-J2EE全部学习笔记 培训结构的学习资料

    2. **Java SE进阶**:这部分可能深入到Java的高级特性,如反射、动态代理、注解、枚举、NIO(New IO)和并发编程。这些知识对于理解和编写高效、灵活的Java代码至关重要。 3. **J2EE组件**:J2EE包含了许多服务器端...

    个人Java学习过程中所有学习笔记

    这份"个人Java学习过程中所有学习笔记"包含了作者在学习Java时积累的宝贵经验,旨在帮助初学者或有经验的程序员巩固基础,提升技能。 笔记内容可能涵盖以下几个主要部分: 1. **Java基础**:这部分通常包括Java的...

    java中的并发变成学习笔记3

    Java中的多线程编程是Java开发中的重要组成部分,它允许应用程序同时执行...以上就是Java多线程编程中关于死锁的原理、示例以及并发编程的一些最佳实践。理解并掌握这些知识点对于编写高效、稳定的多线程程序至关重要。

    Java线程编程学习笔记(二)

    这篇“Java线程编程学习笔记(二)”很可能是对Java并发编程深入探讨的一部分,特别是涉及多线程示例的实践应用。我们将从标题、描述以及标签来推测可能涵盖的知识点,并结合"Multi-Threads Demo"这一压缩包文件名来...

    JAVA入门实例代码及学习笔记

    【JAVA入门实例代码及学习笔记】是一份针对初学者精心准备的资料,涵盖了JAVA语言的基础到进阶的各种重要概念和实战技巧。这份资料通过实际的代码示例和详细的学习笔记,帮助初学者深入理解JAVA编程。 1. **面向...

    《IT学习资料3》-java后端学习笔记.zip

    【标题】"《IT学习资料3》-java...综上,这个压缩包提供的资料覆盖了从基础到进阶的Java后端开发知识,包括版本控制、框架应用、并发编程、数据库管理和项目实践,对于想要深入学习Java后端的人来说是一份宝贵的资源。

Global site tag (gtag.js) - Google Analytics