- 浏览: 2467118 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (574)
- Book (62)
- Architecture (6)
- Java (39)
- Taobao (41)
- Distributed (4)
- Life (72)
- Database (7)
- Spring (16)
- Photography (15)
- Bicycle (41)
- Test (20)
- jBPM (8)
- Business (12)
- Movie (3)
- Ajax (15)
- Code (7)
- Eclipse (96)
- VIM (2)
- Music (6)
- Groovy (10)
- AutoHotKey (3)
- Dorado (10)
- Maven (7)
- Scrum (5)
- English (20)
- Financial (12)
- OSGi (3)
- Other (4)
- Tool (6)
- Browser (1)
- PPT (1)
- Project Management (4)
- Agile (6)
- Nosql (1)
- Search engine (6)
- Shell (2)
- Open Source (4)
- Storm (10)
- Guava (3)
- Baby (1)
- netty (1)
- Algorithm (1)
- Linux (1)
- Python (2)
最新评论
-
roy2011a:
https://github.com/ebottabi/sto ...
storm的序列化问题及与spring的结合方式 -
roy2011a:
能抗能打 写道哥们儿,你好!能共享下那个storm与sprin ...
storm的序列化问题及与spring的结合方式 -
Alick1:
兄弟,你之前是不是在深圳的正阳公司呆过啊?
storm的ack和fail -
liuleixwd:
先点个赞,写的非常好!有个问题请教下,如果我再bolt里不用e ...
storm的ack和fail -
yao-dd:
solr的facet查询
本书基本上是围绕线程和同步, 锁来讲如何实现并发编程, 并结合一些设计模式从中找到一些并发编程的规律, 加以总结即成此书. 而且由于作者也是concurrent包的贡献者, 因此里面也基本上是结合concurrent中的一些实现来做例子.
看完了这本书, 总的感觉是第二章和第四章的内容不错(有些内容需要反复阅读才能理解), 第二章说的是独占, 主要针对synchronized的用法, 第四章是创建线程, 实际上是线程的一些高级用法, 学到不少东东, 第三章看得让人崩溃,不知所云:(.
还有作者总结的原则不是很明显, 没有像23种设计模式那样严谨和中规中矩.导致的后果是看晦涩难懂的文字还不如直接看程序更容易理解作者讲的是什么意思.
synchronized和锁
Object类和他的子类的每一个实例在进入一个同步(synchronized)方法前加锁, 并在离开这个方法时自动释放这把锁. 同步块需要一个参数来指明对哪一个对象加锁. 最常用的参数是this, 它意味着锁住当前正在执行的方法所属的对象. 当一个线程持有一把锁之后, 其他线程必须阻塞, 等待这个线程释放这把锁. 加锁对于非同步的方法不起作用. 因此即便另一个线程持有这个锁, 这个非同步方法也可以执行.
同步的一些简单的原则
永远只是在更新对象的成员变量时加锁.
永远只是在访问有可能被更新的对象的成员变量时才加锁.
永远不要在调用其他对象的方法时加锁.
不变性
如果一个对象的状态不能被改变, 那么它永远也不会遇到由于多个操作以不同的方式改变其状态而导致的冲突和不一致的现象.
具有不变性的最简单的做法就是对象中根本没有数据, 因此他们的方法都是没有状态的, 也就是说这些方法不依赖于任何对象的任何数据.
在构造函数结束之前, 最好禁止访问对象的数据.
对象和锁
包含基本类型的数组对象也是拥有锁的对象, 但是他的每一个基本元素却没有锁(不能把数组元素声明为volatile).
synchronized关键字不属于方法签名的一部分, 所以当子类覆盖父类方法时, synchronized修饰符不会被继承. 因此接口中的方法不能声明为synchronized.同样地, 构造函数也不能声明为synchronized(尽管构造函数中的程序块可以声明为synchronized).
子类和父类的方法实用同一个锁, 但是内部类的锁和他的外部类无关. 然而一个非静态的内部类可以锁住它的外部类. 就像下面的样子:
synchronized(OuterClass.this){...}
锁操作基于"每线程"而不是基于"每调用"
synchronized和atomic不是等价的, 但是同步可以实现原子操作.
遍历的三种同步处理
(1)就是将整个遍历操作方法加上synchronized
(2)在遍历循环内部加synchronized, 比如这样写:
for(int i = 0; true; i++){ Object obj = null; synchronized(v){ if (i < v.size()){ obj = v.get(i); }else{ break; } } }
另外一个极端的做法:
synchronized(v) { snapshot = new Object[v.length]; for (int i = 0; i < snapshot.length; i++) { snapshot[i] = v[i]; } } for (Object object : snapshot) { ... }
(3)失败即放弃, 采用版本化的迭代变量
public class ExpandableArray { private int version = 0; private int cap = 0; private Object[] data; private int size = 0; public int size() { return size; } public synchronized Object get(int i) { return data[i]; } public synchronized void add(Object x) { // data[size++] = x; version++; } public ExpandableArray(int cap) { this.cap = cap; } public synchronized void removeLast() { data[--size] = null; ++version; } public synchronized Iterator iterator() { return new EAIterator(); } protected class EAIterator implements Iterator { private final int currentVersion; private int currentIndex = 0; public EAIterator() { currentVersion = version; } public boolean hasNext() { return currentIndex < size; } public Object next() { synchronized(ExpandableArray.this) { if (currentVersion != version) { throw new RuntimeException("concurrent modified error"); }else if (currentIndex == size) { throw new RuntimeException("no such element"); }else { return data[currentIndex++]; } } } public void remove() { } } }
静态和单例
完全初始化静态变量并不会增加很多系统的启动开销, 除非初始化既消耗资源又很少需要, 否则简单的方式就是把单例数据声明为static final类型
如果每个线程创建一个实例比每个程序创建一个实例更恰当, 这时用ThreadLocal比用单例更恰当.
死锁
死锁是在两个或多个线程都有权限访问两个或多个对象, 并且每个线程都已经得到一个锁的情况下等待其他线程已经得到的锁
java内存模型
为了更好的理解java内存模型, 我们假设每个线程都运行在不同的CPU上, 这样可以更好理解java内存模型的合理性:持有线程的每个CPU都有自己独立的寄存缓存器, 同时所有的CPU都需要跟共享的主存储器通讯来交换数据.
volatile
如果每一个成员变量都声明为volatile, 那么在写线程做下一步存储操作之前, 吸入这个volatile成员变量的数据在主存储器中刷新, 并使其对其他线程可见, 也就是说, 为了这个目的, 成员变量会立即被刷新. 读线程在每次使用volatile成员变量之前都要重新读入数据.
对没有完全没有建好的对象进行引用很不好, 在构造函数内部启动一个线程也是很危险, 尤其是当一个类可能被子类化的时候.
当一个线程结束的时候, 所有写入数据都将被刷新到主存储器中.
把一个数据声明为volatile和同步的区别只是没有使用锁. 尤其对于复合读写操作, 例如对volatile变量进行++操作在执行时并不具有原子性.
因为没有锁的参与, 所以使用volatile比同步要划算一些, 或者至少开销是相同的. 然而如果在方法内部频繁使用volatile成员变量, 则整个性能就会下降. 这时整个方法声明为同步会更好些.
如果仅仅需要保证在多个线程之间正确访问成员变量
ThreadLocal
ThreadLocal类内部维护了一张相关数据(Object引用)和Thread实例列表, 其中的set和get方法可以存取当前Thread空值的数据. 从ThreadLocal类继承来的InheriableThreadLocal类可以自动的把本线程的变量传递给创建的任何一个线程.
很多实用ThreadLocal的设计都被视为单例的扩展. 多数ThreadLocal应用程序为每个线程创建了一个资源的事例, 而不是为每个程序创建一个, ThreadLocal变量通常被声明为静态的, 并且是包范围内可见, 所以这些变量可以在运行于某个线程的一组方法中访问.
令牌
在某些语境或上下文中, 包含独占资源的协议被称作令牌, 一些并行和分布式算法依赖于下面的思想:一个时刻只有一个对象拥有令牌
乐观更新(非阻塞)
乐观更新的一般原理:
1.得到当前状态表示的一个copy(在持有锁的时候)
2.创建一个新的状态表示(不持有任何锁)
3.只有在老状态在被获取后且没有被更新的情况下, 才能被转换成新的状态.
这个也是并发包中的atomic class的compareAndSet()方法的实现原理
public class OptimisticDot { static class ImmutablePoint { int x, y; ImmutablePoint(int x, int y) { this.x = x; this.y = y; } } ImmutablePoint loc; public OptimisticDot(int x, int y) { loc = new ImmutablePoint(x, y); } public synchronized ImmutablePoint location() { return loc; } public synchronized boolean commit(ImmutablePoint assumed, ImmutablePoint next) { if (loc == assumed) { loc = next; return true; }else { return false; } } public synchronized void moveTo(int x, int y) { loc = new ImmutablePoint(x, y); } public void shiftX(int data) { boolean success = false; do { ImmutablePoint old = location(); ImmutablePoint next = new ImmutablePoint(old.x + data, old.y); success = commit(old, next); }while(success); } }
关于线程的设计模式
每消息一线程
public class Host { interface Handler{ void handle(); } Handler handler = new Handler() { @Override public void handle() { // do something } }; private void updateState() { //update state... } public void req() { updateState(); new Thread() { @Override public void run() { handler.handle(); } }.start(); } }
多个并发任务能比同样多的任务串行执行运行的更快, 这种策略能提高系统的吞吐量, 通常这些任务是与IO资源或者计算资源有关, 并且运行在一个多处理器的系统上, 而且客户端也无序等待彼此的任务完成.
缺点是由于创建线程远比直接方法调用开销要大, 所以每消息一线程会增加请求的反应时间.
为了增加扩展性, 将Thread加以封装, 引入一个Executor:
public class HostWithExecutor extends Host{ interface Executor{ void execute(Runnable r); } class DefaultExecutor implements Executor{ @Override public void execute(Runnable r) { new Thread(r).start(); } } private Executor executor; public HostWithExecutor(Executor executor) { super(); this.executor = executor; } public void req() { updateState(); executor.execute(new Runnable() { @Override public void run() { handler.handle(); }}); } }
工作者线程
与前面的不同之处在于, Executor的实现中包含了一个队列来执行不断接收到的请求, 这个队列池就被称之为工作者线程.
public class PlainWorkerPool implements Executor{ protected final Queue<Runnable> queue; public PlainWorkerPool(Queue<Runnable> queue, int workerCount) { this.queue = queue; for(int i = 0; i < workerCount; i++) { activate(); } } private void activate() { new Thread() { @Override public void run() { for(;;) { Runnable r = queue.poll(); r.run(); } } }.start(); } @Override public void execute(Runnable r) { queue.add(r); } }
完成回调
回调实际上是一种观察者模式的实现.
描述:当客户端向服务器端发送一个单向消息的方式激活一个任务, 服务器端在完成任务之后, 向客户端发送一个单项回调消息通知任务已经完成.
适用场景:当需要读入一个特定的文件, 但是IO操作非常的缓慢, 在这个过程中, 你又不想让程序"死"在那里(程序无法响应其他操作), 一种解决办法就是创建一个文件读取服务(FileReader), 当服务完成之后, 向应用程序发送一条消息, 而此后应用程序就可以执行相应的功能了.
public interface FileReader { void read(String fileName, FileReaderClient client); } public interface FileReaderClient { void readCompleted(String fileName, byte[] data); void readFailed(String fileName, IOException ex); } public class FileReaderImpl implements FileReader { @Override public void read(String fileName, FileReaderClient client) { byte[] buffer = new byte[1024]; try { FileInputStream fis = new FileInputStream(fileName); fis.read(buffer); client.readCompleted(fileName, buffer); }catch (IOException e) { client.readFailed(fileName, e); } } } public class FileReaderClientImpl implements FileReaderClient { FileReader reader; public void read(String fileName) { reader.read(fileName, this); } @Override public void readCompleted(String fileName, byte[] data) { //TODO } @Override public void readFailed(String fileName, IOException ex) { //TODO } }
协作线程
这个这个模式就是讲的Thread.join的用法, 将一个耗时的操作封装在一个线程中执行, 然后接着继续主线程的执行, 当需要耗时线程的执行结果时join一下, 从耗时线程中获得最终所需要的结果, 进行主线程后面的内容.
作者用一个获取图片数据, 并加以显示的例子来说明协作线程模式:
public interface Pic { byte[] getImage(); } public interface Randerer { Pic rander(URL src); } public class PictureApp { final Randerer renderer = new StandardRenderer(); public void show(final URL source) { class Waiter implements Runnable{ private Pic result = null; public Pic getResult() { return result; } @Override public void run() { result = renderer.rander(source); } } Waiter waiter = new Waiter(); Thread t = new Thread(waiter); t.start(); // other do something try { t.join(); } catch (Exception e) { return; } Pic pic = waiter.getResult(); // show the pic } }
Future
future是Thread.join的另一种替代物, 它将等待过程封装到了获取图片结果里面. 就是这段代码:
Waiter waiter = new Waiter(); Thread t = new Thread(waiter); t.start(); // other do something try { t.join(); } catch (Exception e) { return; }
对于应用程序来说, 它不再需要知道是否需要经过等待才能拿到结果. 更不需要知道取图片的内容处理原理, 这样对使用者来说更简单.
public class PictureAppWithFuture { private final Renderer renderer = new AsyncRenderer(); public void show(final URL src) { Pic pic = renderer.render(src); //do something // 既不需要专门的另起一个线程, 也不需要通过join等子线程执行完 byte[] image = pic.getImage(); if (image != null) { System.out.println("show image "); } } public static void main(String[] args) throws Exception { PictureAppWithFuture app = new PictureAppWithFuture(); app.show(new URL("url string")); } } public class AsyncRenderer implements Renderer { private final Renderer renderer = new StandardRenderer(); static class FuturePic implements Pic{ private Pic pic = null; private boolean ready = false; synchronized void setPic(Pic pic) { this.pic = pic; ready = true; notifyAll(); } @Override public byte[] getImage() { // 原来的等待操作被封装在这里 while(!ready) { try { wait(); } catch (Exception e) { //return null; } } return pic.getImage(); } } @Override public Pic render(final URL src) { final FuturePic pic = new FuturePic(); // 另起一个线程去拿图片内容 new Thread(new Runnable() { @Override public void run() { // 耗时的操作 pic.setPic(renderer.render(src)); }}).start(); return pic; } }
评论
2 楼
mengsina
2013-03-18
future是Thread.join的另一种替代物
有道理
有道理
1 楼
pan_java
2009-06-04
对多线程的了解又加深了一步.之前看过多线程的设计模式也是受益非浅.不过是基于java1.4,没有学习到新特性的一些应用!
发表评论
-
<异类>读书笔记
2013-03-06 07:54 0成功者能够获得更多的机会,从而能变得更为成功。税收愈减免,富人 ... -
《python学习手册》学习笔记
2013-03-11 22:25 3465python格式化传参数非常赞,用数字标明位置,值得java学 ... -
<万历十五年>读书笔记
2013-03-11 22:27 1583在网上下了一个电子书, 但是貌似跟万历十五年没啥关系, 都是讨 ... -
《鸟哥的linux私房菜》读书笔记(部分)
2013-03-11 22:27 2062x86是一种微机系统硬件架构,另一种是苹果的mac的架构 l ... -
《你的灯亮了吗》读书笔记
2013-03-06 07:20 1503这是一本原本写给程序员的书 本书的四个问题: 搞清问题的来源 ... -
《小狗钱钱》读书笔记
2013-03-06 07:17 1476一本非常不错的理财学习入门书, 以童话的形式, 儿童的思维方式 ... -
《我的奋斗》读书笔记
2012-04-14 22:03 2053文字写的很幽默, 故事也基本都是一些平常人的故事,看到了一个特 ... -
《Java Performance》书评
2012-01-15 18:32 2960原文: http://java.dzone.com/rev ... -
《程序员应该知道的97件事》读书笔记
2012-01-15 18:36 2382一本关于写代码的文 ... -
《影响力》读书笔记
2011-11-05 14:47 1833从书名上很可能以为 ... -
《浪潮之巅》读书笔记
2011-11-05 14:44 1371作为一个中国人通过分析硅谷高科技公司的一系列传奇, 总结出这 ... -
《黑客与画家》读书笔记
2011-11-05 13:37 1817以前看过《rework》, 觉得是每一个小型创业公司的创业宝 ... -
《乔布斯传》读书笔记
2011-10-18 08:53 2845在ipad上看完了这本书, 写的还不错, 里面没有无聊的八 ... -
《细说Java》读书笔记
2011-10-05 15:01 1992国人写的, 感觉是一 ... -
《敏捷估计与规划》读书笔记
2011-10-05 12:08 3177这本书断断续续看了很长时间, 内容非常不错, 基本涵盖了sc ... -
《怪诞心理学》读书笔记
2011-10-05 09:44 1822既然是怪诞, 那么整本书涉及的内容并不是我们平常司空见怪的一 ... -
《番茄工作法图解》读书笔记
2011-09-28 09:02 2390番茄工作法是时间管 ... -
《Java开发超级工具集》读书笔记
2011-09-28 08:59 2098"工欲善其事必先利其器", 在平时的开发 ... -
《敏捷迭代开发管理者指南》读书笔记
2011-09-24 13:09 2214这是一本关于迭代开发 ... -
《解析极限编程》读书笔记
2011-09-24 13:03 1784不知道是kent beck的语 ...
相关推荐
这本书的读书笔记涵盖了多个关键知识点,旨在帮助读者深入理解Java并发编程的核心概念。 1. **线程和进程的区别** - **线程** 是程序执行的最小单位,一个进程中可以有多个线程同时执行,共享同一块内存空间,通信...
通过学习上述知识点,并结合"Java并发编程学习笔记"中的内容,开发者可以更深入地理解Java并发编程,从而设计出更加高效、稳定的并发程序。无论是对初学者还是经验丰富的开发者来说,这本书都是一份宝贵的参考资料。
Java并发编程是指在Java语言中编写多线程和多任务执行的程序,以便更高效地利用计算机的多核处理器资源。并发编程是Java高级编程技能中的重要组成部分,尤其是在需要处理大量数据、提供快速响应、实现高吞吐量和高可...
本文将基于文档《Java并发编程与高并发解决方案-学习笔记***.pdf》中提供的内容,来详细阐述并发编程和高并发的基本概念、CPU多级缓存与缓存一致性、以及Java内存模型。 ### 并发与高并发概念 在现代多线程编程中...
### Java并发编程知识点详解 #### 一、线程状态与管理 在Java中,线程具有多种状态,这些状态的变化反映了线程在其生命周期中的不同阶段。理解这些状态及其转换对于编写高效、健壮的并发程序至关重要。 - **NEW**...
在Java并发编程中,数据的封装与访问控制、线程安全性的考量、同步机制的使用是重要的基础概念和技巧。以下是从给出的文件内容中提取出的详细知识点: 1. 数据封装与访问控制:确保内部私有数据不被轻易访问,并且...
### Java并发编程实践笔记知识点详解 #### 一、保证线程安全的方法 1. **不要跨线程访问共享变量:** 当多个线程共享某个变量时,若其中一个线程修改了该变量,其他线程若没有正确同步,则可能读取到错误的数据。...
Java并发编程实践笔记是一份关于Java并发编程的实践笔记,涵盖了多种关于线程安全、并发编程的实践经验和原则。下面是从笔记中总结的知识点: 1. 保证线程安全的三种方法:不要跨线程访问共享变量,使用final类型的...
《java并发编程实战》读书笔记-第3章-对象的共享,脑图形式,使用xmind8制作 包括线程安全类设计、实例封闭、线程安全性委托、现有线程安全类中添加功能和文档化同步策略等内容
Java并发编程与高并发解决方案是开发高性能应用的关键技术。在基础篇中,主要涉及以下几个重要知识点: 1. **并发编程基础** - **并发**:并发是指在一个时间段内,多个线程交替执行,使得系统看起来像是同时处理...
读书笔记:java学习笔记(包括设计模式并发编程网络编程等内容
Java并发编程是Java开发中的重要领域,它涉及到多线程、同步、锁机制、线程池等关键概念,是提高程序性能和效率的关键技术。在Java中,并发编程的运用可以充分利用多核处理器的能力,实现高效的多任务处理。以下是对...
《Java并发编程实践》这本书是Java开发者深入理解并发编程的重要参考。以下是对书中关键知识点的总结: 1. **线程和进程的区别** - **线程**:是程序执行的最小单位,一个进程中可以有多个线程,它们共享同一块...
读书笔记:Java高并发编程详解多线程与架构设计
### Java并发编程与高并发解决方案知识点总结 #### 一、并发与高并发基本概念 ##### 1.1 并发 - **定义**: 指一个程序在同一时刻拥有两个或更多的线程,这些线程可以在单核或多核处理器上运行。 - **单核处理器上...
Java并发编程 背景介绍 并发历史 必要性 进程 资源分配的最小单位 线程 CPU调度的最小单位 线程的优势 (1)如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率 ...
### Java编程思想读书笔记 #### 一、Java与C++的区别及内存管理 在学习Java的过程中,我们常常会拿它与C++进行比较。这两门语言虽然有着相似之处,但也有许多不同点。 1. **内存管理:** - C++提供了更为底层的...
读书笔记:参考Java高并发编程详解多线程与架构设计汪文君学习笔记及源码
读书笔记:算法 并发 函数式编程 java语言拾遗 手写设计模式
Java并发编程是开发人员在构建高并发应用时必须掌握的关键技术。高并发环境下,接口幂等性成为确保系统稳定性和正确性的必要条件。幂等性指的是一个操作无论执行多少次,其结果始终相同,这对于避免重复操作导致的...