湖畔镇

Java——多线程

一个程序可能包含多个并发运行的任务,可以在程序中并发的启动多个线程

创建任务和线程

为了创建任务,必须为任务定义一个类,任务类必须实现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()) {
        ......
    }
}
分享