Java多线程:线程带来的问题

安全性问题


毫无疑问,线性安全是很重要的。那么如何判断一个类是线性安全的呢?

当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程将如何交替执行,并且在主调用代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线性安全的。

解决线程安全性问题可以用前面提到的加锁机制,包括类内置的锁和显式锁。

活跃性问题


死锁


一个具体死锁的例子:

Java多线程:新特性—同步工具

CountDownLatch


CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。

假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

1
2
3
4
5
6
7
8
public CountDownLatch(int count);
构造方法,指定了计数的次数
public void countDown();
当前线程调用此方法,则计数减一
public void await();
调用此方法会一直阻塞当前线程,直到计时器的值为0

Java多线程:新特性—有返回值的线程

概念


Java5增加了有返回值的线程。可返回值的任务(线程)必须实现Callable接口,类似的,无返回值的任务(线程)必须Runnable接口。

执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。

例子


Java多线程:新特性—线程池

概念


线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。

当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

Java5的线程池分好多种:固定尺寸的线程池可变尺寸连接池、当然你还可以自定义线程池

在使用线程池之前,必须知道如何去创建一个线程池,在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法,是必须掌握的。

固定大小的线程池


Java多线程:新特性—原子变量和CAS

硬件对并发的支持


现在,几乎所有的现代处理器中都包含了某种形式的原子读-改-写指令,例如比较和交换(CAS)或者关联加载和条件存储(Load-Linked/Store-Conditional)。

操作系统和JVM可以使用这些指令来实现锁和并发的数据结构。例如Java的ConcurrentlinkedQueue就是使用CAS实现的。

Java5以后也提供了对这些指令的支持。当多个线程尝试使用CAS同时更新同一个变量时候,只有一个线程能更新变量的值,而其他线程都将失败。

然而,失败的线程并不会被挂起(这与获取锁的情况不同:当获取锁失败时,线程将会被挂起),而是被告知在这次竞争中失败,并且可以重试。

由于一个线程在竞争CAS时失败不会被阻塞,因此它可以决定是否重新尝试,或者执行一些恢复操作,也或者不执行任何操作。

Fork me on GitHub