‘ 并发译文 ’ 目录归档
解析Disruptor的依赖关系
原文地址:http://ifeve.com/dissecting-disruptor-wiring-up/
作者:Trisha 译者:廖涵 校对:方腾飞
现在我已经讲了 RingBuffer 本身,如何从它 读取 以及如何向它 写入。从逻辑上来说,下一件要做的事情就是把所有的东西拼装到在一起。
我前面提到过多生产者的情况——他们通过 ProducerBarrier 保证写入操作顺序与可控。我也提到过简单场景下的多消费者数据访问。更多的消费者的场景会变得更加复杂,我们 实现了一些聪明的机制允许多个消费者在访问 Ring Buffer 的时候互相等待(依赖)。像很多应用里,有一连串的工作需要在实际执行业务逻辑之前完成 (happen before) —— 例如,在做任何操作之前,我们都必须先保证消息写入磁盘。
Disruptor 论文 和性能测试里包含了你可能想到的一些基本结构。我准备讲一下其中最有趣的那个,这多半是因为我需要练习如何使用画图板。 阅读全文
饥饿和公平
原文地址 By Jakob Jenkov 翻译 Simon-SZ 校对:方腾飞
如果一个线程因为CPU时间全部被其他线程抢走而得不到CPU运行时间,这种状态被称之为“饥饿”。而该线程被“饥饿致死”正是因为它得不到CPU运行时间的机会。解决饥饿的方案被称之为“公平性” – 即所有线程均能公平地获得运行机会。
下面是本文讨论的主题:
1. Java中导致饥饿的原因:
- 高优先级线程吞噬所有的低优先级线程的CPU时间。
- 线程被永久堵塞在一个等待进入同步块的状态。
- 线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的wait方法)。
2. 在Java中实现公平性方案,需要:
- 使用锁,而不是同步块。
- 公平锁。
- 注意性能方面。 阅读全文
dissecting-disruptor-wiring-up
原文地址:http://mechanitis.blogspot.com/2011/07/dissecting-disruptor-wiring-up.html (因被墙移到墙内)
So now I’ve covered the ring buffer itself, reading from it and writing to it.
Logically the next thing to do is to wire everything up together.
I talked about multiple producers – they have the producer barrier to keep them in order and under control. I’ve talked about consumers in a simple situation. Multiple consumers can get a little more involved. We’ve done some clever stuff to allow the consumers to be dependent on each other and the ring buffer. Like a lot of applications, we have a pipeline of things that need to happen before we can actually get on with the business logic – for example, we need to make sure the messages have been journalled to disk before we can do anything. 阅读全文
可见性问题实例
说到并发安全时,我们常提及可见性的问题,通俗点讲就是线程1看不到线程2写入变量v的值(更专业的解释以及是什么导致可见性问题,又该如何解决,见扩展阅读),但一直偏于理论,实际中有没有因可见性而导致问题的例子呢?回答是肯定的,接下来我们一起来看几个例子。
这个例子很简单,新建的线程里有一个普通变量stop,用来表示是否结束循环里的自增操作。主线程启动这个线程后,将该变量置为true,观察线程是否打印出finish loop那行,如果存在可见性问题,主线程修改stop值为true,线程v看stop的值应该还是false。
[code lang=”java”]
class VisibilityThread extends Thread {
private boolean stop;
public void run() {
int i = 0;
System.out.println("start loop.");
while(!getStop()) {
i++;
}
System.out.println("finish loop,i=" + i);
}
public void stopIt() {
stop = true;
}
public boolean getStop(){
return stop;
}
}
public class VisibilityTest {
public static void main(String[] args) throws Exception {
VisibilityThread v = new VisibilityThread();
v.start();
Thread.sleep(1000);//停顿1秒等待新启线程执行
System.out.println("即将置stop值为true");
v.stopIt();
Thread.sleep(1000);
System.out.println("finish main");
System.out.println("main中通过getStop获取的stop值:" + v.getStop());
}
}
[/code]
嵌套管程锁死
原文链接 作者:Jakob Jenkov
译者:余绍亮 校对:丁一
嵌套管程锁死类似于死锁, 下面是一个嵌套管程锁死的场景:
线程1获得A对象的锁。 线程1获得对象B的锁(同时持有对象A的锁)。 线程1决定等待另一个线程的信号再继续。 线程1调用B.wait(),从而释放了B对象上的锁,但仍然持有对象A的锁。 线程2需要同时持有对象A和对象B的锁,才能向线程1发信号。 线程2无法获得对象A上的锁,因为对象A上的锁当前正被线程1持有。 线程2一直被阻塞,等待线程1释放对象A上的锁。 线程1一直阻塞,等待线程2的信号,因此,不会释放对象A上的锁, 而线程2需要对象A上的锁才能给线程1发信号……
多线程的代价
原文链接:http://tutorials.jenkov.com/java-concurrency/costs.html
作者:Jakob Jenkov 翻译:古圣昌 校对:欧振聪
从一个单线程的应用到一个多线程的应用并不仅仅带来好处,它也会有一些代价。不要仅仅为了使用多线程而使用多线程。而应该明确在使用多线程时能多来的好处比所付出的代价大的时候,才使用多线程。如果存在疑问,应该尝试测量一下应用程序的性能和响应能力,而不只是猜测。 阅读全文
线程池
原文地址:jenkov 作者: Jakob Jenkov 译者:长源 校对:方腾飞
线程池(Thread Pool)对于限制应用程序中同一时刻运行的线程数很有用。因为每启动一个新线程都会有相应的性能开销,每个线程都需要给栈分配一些内存等等。
我们可以把并发执行的任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程。只要池里有空闲的线程,任务就会分配给一个线程执行。在线程池的内部,任务被插入一个阻塞队列(Blocking Queue ),线程池里的线程会去取这个队列里的任务。当一个新任务插入队列时,一个空闲线程就会成功的从队列中取出任务并且执行它。
Java中的读/写锁
原文链接 作者:Jakob Jenkov 译者:微凉 校对:丁一
相比Java中的锁(Locks in Java)里Lock实现,读写锁更复杂一些。假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)。这就需要一个读/写锁来解决这个问题。
Java5在java.util.concurrent包中已经包含了读写锁。尽管如此,我们还是应该了解其实现背后的原理。
以下是本文的主题
- 读/写锁的Java实现(Read / Write Lock Java Implementation)
- 读/写锁的重入(Read / Write Lock Reentrance)
- 读锁重入(Read Reentrance)
- 写锁重入(Write Reentrance)
- 读锁升级到写锁(Read to Write Reentrance)
- 写锁降级到读锁(Write to Read Reentrance)
- 可重入的ReadWriteLock的完整实现(Fully Reentrant ReadWriteLock)
- 在finally中调用unlock() (Calling unlock() from a finally-clause)
Java并发性和多线程介绍
作者:Jakob Jenkov 译者:Simon-SZ 校对:方腾飞
http://tutorials.jenkov.com/java-concurrency/index.html
在过去单CPU时代,单任务在一个时间点只能执行单一程序。之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程。虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一定的时间片运行。
随着多任务对软件开发者带来的新挑战,程序不在能假设独占所有的CPU时间、所有的内存和其他计算机资源。一个好的程序榜样是在其不再使用这些资源时对其进行释放,以使得其他程序能有机会使用这些资源。
再后来发展到多线程技术,使得在一个程序内部能拥有多个线程并行执行。一个线程的执行可以被认为是一个CPU在执行该程序。当一个程序运行在多线程下,就好像有多个CPU在同时执行该程序。
多线程比多任务更加有挑战。多线程是在同一个程序内部并行执行,因此会对相同的内存空间进行并发读写操作。这可能是在单线程程序中从来不会遇到的问题。其中的一些错误也未必会在单CPU机器上出现,因为两个线程从来不会得到真正的并行执行。然而,更现代的计算机伴随着多核CPU的出现,也就意味着不同的线程能被不同的CPU核得到真正意义的并行执行。
如果一个线程在读一个内存时,另一个线程正向该内存进行写操作,那进行读操作的那个线程将获得什么结果呢?是写操作之前旧的值?还是写操作成功之后的新值?或是一半新一半旧的值?或者,如果是两个线程同时写同一个内存,在操作完成后将会是什么结果呢?是第一个线程写入的值?还是第二个线程写入的值?还是两个线程写入的一个混合值?因此如没有合适的预防措施,任何结果都是可能的。而且这种行为的发生甚至不能预测,所以结果也是不确定性的。 阅读全文