一个程序可能包含多个并发运行的任务,可以在程序中并发的启动多个线程
创建任务和线程
为了创建任务,必须为任务定义一个类,任务类必须实现Runnable
接口
public class TaskClass implements Runnable {
@Override
public void run() {
...
}
}
任务必须在线程中进行
TaskClass task = new TaskClass();
Thread thread = new Thread(task);
使用start()
方法运行任务
thread.start();
Thread类
Thread
类实现了Runnable
,所以可以定义一个Thread
的扩展类,并且实现run()
方法,然后创建该类的一个对象,调用start()
来启动线程,然而并不推荐这么做,因为它将任务和运行任务的机制混在了一起,将任务从线程中分离出来是更好的设计
stop()
、suspend()
、resume()
普遍被认为不安全,为替代stop()
的使用,可以通过给Thread
变量赋值null
来表明它被停止
可以使用join()
方法等待另一个线程的结束
为避免竞争,高优先级的线程必须定时调用sleep()
或yield()
方法,来给低优先级的线程一个执行机会
线程池
线程池是管理并发执行任务个数的理想方法,Executor
接口用来执行线程池中的任务,ExecutorService
接口用来管理和控制任务
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.execute(new TaskClass());
executor.execute(new TaskClass());
executor.execute(new TaskClass());
executor.shutdown();
顺序执行,每次一个任务:
ExecutorService executor = Executors.newFixedThreadPool(1);
所有任务并发进行:
ExecutorService executor = Executors.newCachedThreadPool();
shutdown()
方法用来通知执行器关闭,不再接受新的任务,但是现有任务会继续执行
线程同步
共享资源被多个线程同时访问,可能产生竞争状态,导致线程不安全
应该放置多个线程同时进入程序的特定部分,称为临界区
,可以使用关键字synchronized
来同步方法,使得一次只有一个线程可以访问这个方法
public synchronized void deposit(double mount);
也可以使用同步块:
synchronized(expression) {
......
}
允许设置同步方法中的部分代码而不是整个方法,大大增强了程序的并发能力
利用加锁同步
可以显示的加锁,给协调线程带来了更多的控制功能。一个锁是一个Lock
接口的实例,定义了加锁和释放的方法
可以使用newCondition()
来创建任意个数的Condition
对象,进行线程通信
ReentrantLock
是为创建相互排斥的锁的Lock
的具体实现
Lock lock = new ReentrantLock();
lock.lock();
......
lock.unlock();
线程间协作
一个线程可以指定在某种条件下该做什么,条件是通过调用newCondition()
方法创建的对象,一旦创建了条件,就可以使用await()
、signal()
、signalAll()
方法来实现线程间通信
await()
可以让当前线程等待,直到条件发生
signal()
唤醒一个等待的线程
signalAll()
唤醒所有等待的线程
条件由Lock
对象创建,为了调用任意方法,必须首先拥有锁
信号量
信号量可以用来限制访问共享资源的线程数,访问资源前,线程必须从信号量获得许可,访问结束后,需要将许可返回给信号量
private static Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
...
semaphore.release();
避免死锁
有时两个或多个线程需要在几个共享对象上获取锁,这可能导致死锁,即两个线程都锁定了一个资源并且在等待对方的资源
可以通过定义资源获取顺序来解决这个问题
线程状态
新建->就绪->运行->阻塞->结束
isAlive()
方法用来判断线程状态,如果就绪、阻塞或运行,则返回true
,如果新建而没启动或解说,则返回false
interrupt()
这样中断一个线程:当线程就绪或运行时,给它设置一个中断标志,线程阻塞时,它将被唤醒被进入就绪状态同时抛出异常InterruptedException
同步集合
Collections
类提供六个静态方法来将集合转换成同步版本
调用synchronizedCollection(Collection c)
会返回一个新的Collection
对象,在它里面所有访问和更新的方法都被同步
使用迭代器访问结合需要做同步
Set hashSet = Collections.synchronizedSet(new HashSet());
synchronized (hashSet) {
Iterator iterator = hashSet.iterator();
while(iterator.hasNext()) {
......
}
}