Skip to main content
Jkyo Chen Blog

第十四章 多线程

多线程 #

中断线程 #

// Thread.currentThread()获取当前线程
while(!Thread.currentThread().isInterrupted() && more work to do)
{
    do more work
}
// 如果线程被阻塞,就无法检测中断状态
java.lang.Thread 1.0
void interrupt() //向线程发送中断请求。线程的中断状态将被设置为true。如果目前该线程被一个sleep调用阻塞,那么,InterruptedException异常被抛出
static boolean interrupted() // 测试当前线程是否被中断。静态方法。副作用:它将当前线程的中断状态重置为false
boolean isInterrupted() // 测试线程是否被终止。这一调用不改变线程的中断状态
static Thread currentThread() // 返回代表当前执行线程的Thread对象

线程状态 #

可运行线程 #

被阻塞线程和等待线程 #

被终止的线程 #

void join() // 等待终止指定的线程
void join(long millis) // 等待指定的线程死亡或者经过指定的毫秒数。
Thread.State getState() 5.0 // 得到这一线程的状态;NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING或TERMINATED之一

线程属性 #

线程优先级 #

java.lang.Thread 1.0
void setPriority(int newPriority) // 设置线程的优先级一般使用Thread.NORM_PRIORITY优先级
static int MIN_PRIORITY // 线程的最小优先级。值为1
static int NORM_PRIORITY // 线程的默认优先级。默认优先级为5
static int MAX_PRIORITY // 线程的最高优先级。值为10
static void yield() 导致当前执行线程处于让步状态。如果有其他的可运行线程具有至少与线程同样高的优先级,那么这些线程接下来会被调度。

守护线程 #

java.lang.Thread 1.0
t.setDaemon(true); // 标识该线程为守护线程(daemon thread)或用户线程。
// 用途是为其他线程提供服务

未捕获异常处理器 #

java.lang.Thread 1.0
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler) 5.0
static ThreadUncaughtExceptionHandler getDefaultUncaughtExceptionHandler() 5.0
// 设置或获取为捕获异常的默认处理器
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler) 5.0
// 设置或捕获异常的处理器。如果没有安装处理器,则将线程组对象作为处理器。

java.lang.Thread.UncaughtExceptionHandler 5.0
void uncaughtException(Thread t, Throwable e)
// 当一个线程因未捕获异常而终止,按规定要将客户报告记录到日志中。t:由于未捕获异常而终止的线程 e:未捕获的异常对象

java.lang.ThreadGroup 1.0
void uncaughtException(Thread t, Throwable e)

同步 #

锁对象 #

// ReentrantLock
myLock.lock(); // a ReentrantLock object
try
{
    critical section
}
finally
{
 myLock.unlock(); // make sure the lock is unlocked even if an exception is thrown
}
// 确保任何时刻只有一个线程进入临界区。
// 一旦一个线程封锁了锁对象,其他任何线程都无法通过lock语句。当其他线程调用lock时,它们被阻塞,直到第一个线程释放锁对象。
// 把解锁对象括在finally子句之内是至关重要的。如果在临界区的代码跑出异常,锁必须别释放。否则,其他线程将永远阻塞。
java.util.concurrent.locks.Lock 5.0
void lock(); // 获得这个锁;如果锁同时被另一个线程拥有则发生阻塞
void unlock(); // 释放这个锁

java.util.concurrent.locks.ReentrantLock 5.0
ReentrantLock(); // 构建一个可以被用来保护临界区等可重入锁
ReentrantLock(boolean fair); // 构建一个带有公平策略的锁。一个公平锁偏爱等待时间最长的线程。

条件对象 #

java.util.concurrent.locks.Lock 5.0
Condition newCondition() // 返回一个与该锁相关的条件对象

java.util.concurrent.locks.Condition 5.0
void await() // 将该线程放到条件的等待集中
void signalAll() // 解除该条件的等待集中的所欲线程的阻塞状态
void signal() // 从该条件的等待集中随机地选择一个线程,解除其阻塞状态

synchronized关键字 #

同步阻塞 #

synchronized(obj)
{
    critical section
}
// 获得obj的锁

监视器概念 #

Volatile 域 #

private boolean done;
public synchronized boolean isDone() { return done; }
public synchronized void setDone() { done = true; }
// 另一个线程已经对该对象加锁,isDone 和 setDone 可能阻塞。

private volatile boolean done;
public boolean isDone() { return done; }
public void setDone() { done = true; }

final变量 #

final Map accounts = new HashMap<>();
// 其他线程会在构造函数完成构造之后才看到这个accounts变量
// 当然对这个映射表的操作并不是安全的。如果多个线程在读写这个映射表,仍然需要进行同步。

原子性 #

死锁 #

线程局部变量 #

public static final ThreadLocal dateFormat =
            new ThreadLocal()
            {
                protected SimpleDateFormat initialValue()
                {
                    return new SimpleDateFormat("yyyy-MM-dd");
                }
            };
// java.util.Random 类是线程安全的。但是如果多个线程需要等待一个共享的随机数生成器,这会很低效。
// 使用ThreadLocal辅助类为各个线程提供一个单独的生成器
// Java SE 7 提供了一个便利类
in random = ThreadLocalRandom.current().nextInt(upperBond);
// ThreadLocalRandom.current() 调用会返回特定于当前线程的 Random 类实例

java.lang.ThreadLocal 1.2
T get() // 得到这个线程的当前值。如果是首次调用get,会调用 initialize 来得到这个值。
protected initialize() // 应覆盖这个方法来提供一个初始值。默认情况下,这个方法返回 null
void set(T t) // 为这个线程设置一个新值
void remove() // 删除对应这个线程的值

java.util.concurrent.ThreadLocalRandom 7
static ThreadLocal Random current() // 返回特定于当前线程的 Random 类实例

锁测试与超时 #

if(myLock.tryLock())
    // now the thread owns the lock
    try { ... }
    finally { myLock.unlock(); }
else
    // do something else

// 使用超时参数
if(myLock.tryLock(100, TimeUnit.MILLSECONDS)) ...
// TimeUnit 是个枚举类型,可以取的值包括 SECONDS, MILLISECONDS, MICROSECONDS 和 NANOSECONDS
java.util.concurrent.locks.Lock 5.0
boolean tryLock()
boolean tryLock(long time, TimeUnit unit) // 尝试获得锁,阻塞时间不会超过给定的值;如果成功返回true
void lockInterruptibly() // 获得锁,但是不确定地发生阻塞。如果线程被中断,抛出一个 InterruptedException 异常

java.util.concurrent.locks.Condition 5.0
boolean await(long time, TimeUnit unit) // 进入该条件等待集,直到线程从等待集中移除或等待了指定的时间之后才解除阻塞。
void awaitUninterruptibly()

读/写锁 #

// 构造一个 ReentrantReadWriteLock 对象
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
// 抽取读锁和写锁:
private Lock readLock = rwl.readLock();
private Lock WriteLock = rwl.writeLock();
// 对所有的获取方法加读锁:
public double getTotalBalance()
{
    readLock.lock();
    try { ... }
    finally { readLock.unlock(); }
}
// 对所有的修改方法加写锁
public void transfer(...)
{
    writeLock.lock();
    try { ... }
    finally { writeLock.unlock();}
}

java.util.concurrent.locks.ReentrantReadWriteLock 5.0
Lock readLock() // 得到一个可以被多个读操作共用的锁,但会排斥所有写操作。
Lock writeLock() // 得到一个写锁,排斥所有其他的读操作和写操作

为什么弃用 stop 和 suspend 方法 #

// 安全地挂起线程,引入一个变量 suspendRequested 并在 run 方法的某个安全的地方测试它。
private volatile boolean suspendRequested = false;
private Lock suspendLock = new ReentrantLock();
private Condition suspendCondition = suspendLock.newCondition();

public void run()
{
    while(...)
    {
        ...
        if(suspendRequested)
        {
            suspendLock.lock();
            try { while(suspendRequested) suspendCondition.await();}
            finally { suspendLock.unlock();}
        }
    }
}
public void requestSuspend() { suspendRequested = true;}
public void requestResume()
{
    suspendRequested = false;
    suspendLock.lock();
    try { suspendCondition.signalAll(); }
    finally { suspendLock.unlock();}
}

阻塞队列 #

线程安全的集合 #

高效的映射表,集合和队列 #

写数组的拷贝 #

较早的线程安全集合 #

Callable 和 Future #

执行器 #

线程池 #

预定执行 #

控制任务组 #

Fork-Join 框架( Java SE 7 ) #

// 有一个处理任务,它可以很自然地分解为子任务
if(problemSize < threshold)
    solve problem directly
else
{
    break problem into subproblems
    recursively solve each subproblem
    combine the results
}

同步器 #

信号量 #

倒计时门栓 #

障栅 #

交换器 #

同步队列 #

线程与 Swing #