Java多线程:线程带来的问题
安全性问题
毫无疑问,线性安全是很重要的。那么如何判断一个类是线性安全的呢?
当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程将如何交替执行,并且在主调用代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线性安全的。
解决线程安全性问题可以用前面提到的加锁机制,包括类内置的锁和显式锁。
活跃性问题
死锁
一个具体死锁的例子:
毫无疑问,线性安全是很重要的。那么如何判断一个类是线性安全的呢?
当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程将如何交替执行,并且在主调用代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线性安全的。
解决线程安全性问题可以用前面提到的加锁机制,包括类内置的锁和显式锁。
CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。
假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()
方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()
方法,这个调用await()
方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。
|
|
Java5增加了有返回值的线程。可返回值的任务(线程)必须实现Callable
接口,类似的,无返回值的任务(线程)必须Runnable
接口。
执行Callable
任务后,可以获取一个Future
的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。
当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
Java5的线程池分好多种:固定尺寸的线程池
、可变尺寸连接池
、当然你还可以自定义线程池
。
在使用线程池之前,必须知道如何去创建一个线程池,在Java5中,需要了解的是java.util.concurrent.Executors
类的API,这个类提供大量创建连接池的静态方法,是必须掌握的。
现在,几乎所有的现代处理器中都包含了某种形式的原子读-改-写指令,例如比较和交换(CAS
)或者关联加载和条件存储(Load-Linked/Store-Conditional
)。
操作系统和JVM可以使用这些指令来实现锁和并发的数据结构。例如Java的ConcurrentlinkedQueue
就是使用CAS实现的。
Java5以后也提供了对这些指令的支持。当多个线程尝试使用CAS同时更新同一个变量时候,只有一个线程能更新变量的值,而其他线程都将失败。
然而,失败的线程并不会被挂起(这与获取锁的情况不同:当获取锁失败时,线程将会被挂起),而是被告知在这次竞争中失败,并且可以重试。
由于一个线程在竞争CAS时失败不会被阻塞,因此它可以决定是否重新尝试,或者执行一些恢复操作,也或者不执行任何操作。