并发译文 ’ 目录归档

Java并发性和多线程介绍目录

concurrency

原文地址   译文地址  译者:张坤等

  1. Java并发性和多线程介绍
  2. 多线程的优点
  3. 多线程的代价
  4. 并发编程模型
  5. 如何创建并运行java线程
  6. 竞态条件与临界区
  7. 线程安全与共享资源
  8. 线程安全及不可变性
  9. Java内存模型
  10. JAVA同步块
  11. 线程通信
  12. Java ThreadLocal
  13. Thread Signaling (未翻译)
  14. 死锁
  15. 避免死锁
  16. 饥饿和公平
  17. 嵌套管程锁死
  18. Slipped Conditions
  19. Java中的锁
  20. Java中的读/写锁
  21. 重入锁死
  22. 信号量
  23. 阻塞队列
  24. 线程池
  25. CAS
  26. 剖析同步器
  27. 无阻塞算法
  28. 阿姆达尔定律

解析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发信号……

阅读全文

线程通信

原文链接  作者:Jakob Jenkov

译者:杜建雄  校对:方腾飞

线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。

例如,线程B可以等待线程A的一个信号,这个信号会通知线程B数据已经准备好了。本文将讲解以下几个JAVA线程间通信的主题:

1、通过共享对象通信

2、忙等待

3、wait(),notify()和notifyAll()

4、丢失的信号

5、假唤醒

6、多线程等待相同信号

7、不要对常量字符串或全局对象调用wait()
阅读全文

多线程的代价

原文链接:http://tutorials.jenkov.com/java-concurrency/costs.html

作者:Jakob Jenkov     翻译:古圣昌        校对:欧振聪

从一个单线程的应用到一个多线程的应用并不仅仅带来好处,它也会有一些代价。不要仅仅为了使用多线程而使用多线程。而应该明确在使用多线程时能多来的好处比所付出的代价大的时候,才使用多线程。如果存在疑问,应该尝试测量一下应用程序的性能和响应能力,而不只是猜测。 阅读全文

重入锁死

原文链接 作者:Jakob Jenkov 译者:刘晓日 校对:丁一

重入锁死与死锁嵌套管程锁死非常相似。读写锁两篇文章中都有涉及到重入锁死的问题。

当一个线程重新获取读写锁或其他不可重入的同步器时,就可能发生重入锁死。可重入的意思是线程可以重复获得它已经持有的锁。Java的synchronized块是可重入的。因此下面的代码是没问题的:

(译者注:这里提到的锁都是指的不可重入的锁实现,并不是Java类库中的Lock与ReadWriteLock类)

阅读全文

线程池

原文地址:jenkov  作者: Jakob Jenkov  译者:长源  校对:方腾飞

线程池(Thread Pool)对于限制应用程序中同一时刻运行的线程数很有用。因为每启动一个新线程都会有相应的性能开销,每个线程都需要给栈分配一些内存等等。

我们可以把并发执行的任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程。只要池里有空闲的线程,任务就会分配给一个线程执行。在线程池的内部,任务被插入一个阻塞队列(Blocking Queue ),线程池里的线程会去取这个队列里的任务。当一个新任务插入队列时,一个空闲线程就会成功的从队列中取出任务并且执行它。

阅读全文

多线程的优点

原文:http://tutorials.jenkov.com/java-concurrency/benefits.html

作者:Jakob Jenkov        翻译:古圣昌            校对:欧振聪

尽管面临很多挑战,多线程有一些优点使得它一直被使用。这些优点是:

  • 资源利用率更好
  • 程序设计在某些情况下更简单
  • 程序响应更快

阅读全文

避免死锁

原文链接        作者:Jakob Jenkov

译者:申章   校对:丁一

在有些情况下死锁是可以避免的。本文将展示三种用于避免死锁的技术:

  1. 加锁顺序
  2. 加锁时限
  3. 死锁检测

阅读全文

Java中的读/写锁

原文链接 作者:Jakob Jenkov 译者:微凉 校对:丁一

相比Java中的锁(Locks in Java)里Lock实现,读写锁更复杂一些。假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)。这就需要一个读/写锁来解决这个问题。

Java5在java.util.concurrent包中已经包含了读写锁。尽管如此,我们还是应该了解其实现背后的原理。

以下是本文的主题

  1. 读/写锁的Java实现(Read / Write Lock Java Implementation)
  2. 读/写锁的重入(Read / Write Lock Reentrance)
  3. 读锁重入(Read Reentrance)
  4. 写锁重入(Write Reentrance)
  5. 读锁升级到写锁(Read to Write Reentrance)
  6. 写锁降级到读锁(Write to Read Reentrance)
  7. 可重入的ReadWriteLock的完整实现(Fully Reentrant ReadWriteLock)
  8. 在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核得到真正意义的并行执行。

如果一个线程在读一个内存时,另一个线程正向该内存进行写操作,那进行读操作的那个线程将获得什么结果呢?是写操作之前旧的值?还是写操作成功之后的新值?或是一半新一半旧的值?或者,如果是两个线程同时写同一个内存,在操作完成后将会是什么结果呢?是第一个线程写入的值?还是第二个线程写入的值?还是两个线程写入的一个混合值?因此如没有合适的预防措施,任何结果都是可能的。而且这种行为的发生甚至不能预测,所以结果也是不确定性的。 阅读全文

死锁

原文链接 作者:Jakob Jenkov 译者:申章 校对:丁一

死锁是两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。

例如,如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一个死锁。

阅读全文

return top