本章内容: * 什么是线程 * 中断线程 * 线程状态 * 线程属性 * 同步 * 阻塞队列 * 线程安全的集合 * Collable与Future * 执行器 * 同步器 * 线程与Swing
t.setDaemon(true)
将线程转换为守护线程( daemon thread )。守护线程的惟一用途是为其余线程提供服务。计时线程就是一个例子,它定时地发送“计时器嘀嗒”信号给其余线程或清空过期的高速缓存项的线程。当只剩下守护线程时,虚拟机就退出了,因为若是只剩下守护线程,就不必继续运行程序了。void uncaughtException(Thread t,Throwable e)能够用 setUncaughtExceptionHandler 方法为任何线程安装一个处理器。也能够用 Thread 类的静态方法 setDefaultUncaughtExceptionHandler 为全部线程安装一个默认的处理器。替换处理器可使用日志API发送未捕获异常的报告到日志文件。
myLock.lock(); //a ReentrantLock object try { critical section } finally { myLock.unlock();//make sure the lock is unlocked even if an exception is three }这一结构确保任什么时候刻只有一个线程进入临界区。一旦一个线程封锁了锁对象,其余任何线程都没法经过 lock 语句。当其余线程调用 lock 时,它们被阻塞,直到第一个线程释放锁对象。
while(!(ok to proceed)) condition.await();
intrinsicCondition.await(); intrinsicCondition.signalAll();
synchronized(obj) //this is the synchronized block { critical section }因而它得到 obj 的锁。
private volatile boolean done; public void flipDone(){done = !done;} //not atomic不能确保翻转域中的值。
final Map<String,Double> accounts = new HashMap();其余线程会在构造函数完成构造以后才看到这个 accounts 变量。
public static final ThreadLocal< SimpleDateFormat > dateFormat = new ThreadLocal< SimpleDateFomrat >() { protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }要访问具体的格式化方法,能够调用:
String dateStamp = dateFormat.get().format(new Date());在一个给定线程中首次调用 get 时,会调用 initilaValue 方法。在此以后, get 方法会返回属于当前线程的那个实例。
int random = ThreadLocalRandom.current().nextInt(upperBound);ThreadLocalRandom.current() 调用会返回特定于当前线程的 Random 类实例。
myCondition.await(100,TimeUnit.MILLISECONDS)若是一个线程被另外一个线程经过调用 signalAll 或 signal 激活,或者超时时限已达到,或者线程被中断,那么 await 方法将返回。
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();(2)抽取读锁和写锁:
private Lock readLock = rwl.readLock(); private Lock writeLock = rwl.writeLock();(3)对全部的获取方法加读锁:
public double getTotalBalance() { readLock.lock(); try{...} finally{readLock.unlock();} }(4)对全部的修改方法加写锁:
public void transfer(...) { writeLock.lock(); try{...} finally{writeLock.unlock();} }
方法 | 正常动做 | 特殊状况下的动做 |
---|---|---|
add | 添加一个元素 | 若是队列满,则抛出IllegalStateException异常 |
element | 返回队列的头元素 | 若是队列空,抛出NoSuchElementException异常 |
offer | 添加一个元素并返回true | 若是队列满,返回false |
peek | 返回队列的头元素 | 若是队列空,则返回null |
poll | 移出并返回队列的头元素 | 若是队列空,则返回null |
put | 添加一个元素 | 若是队列满,则阻塞 |
remove | 移出并返回头元素 | 若是队列空,则抛出NoSuchElementException异常 |
take | 移出并返回头元素 | 若是队列空,则阻塞 |
boolean success = q.offer(x,100,TimeUnit.MILLISECONDS);尝试在100毫秒的时间内在队列的尾部插入一个元素。若是成功返回true;不然,达到超时时,返回false。相似地,下面的调用:
Object head = q.poll(100,TimeUnit.MILLISEDS);尝试用100毫秒的时间移除队列的头元素;若是成功返回头元素,不然,达到在超时时,返回null。
interface Delayed extends Comparable< Delayed > { long getDelay(TimeUnit unit); }getDelay方法返回对象的残留延迟。负值表示延迟已经结束。元素只有在延迟用完的状况下才能从DelayQueue移除。还必须实现compareTo方法。DelayQueue使用该方法对元素进行排序。
q.transfer(item);这个调用会阻塞,直到另外一个线程将元素(item)删除。LinkedTransferQueue实现了这个接口。
cache.putIfAbsent(key,value);相反的操做是删除(或许应该叫作removeIfPresent)。调用
cache.remove(key,value);将原子性地删除键值对,若是它们在映像表中出现的话。最后,
cache.replace(key,oldValue,newValue);原子性地用新值替换旧值,假定旧值与指定的键值关联。
List< E > synchArrayList = Collections.synchronizedList(new ArrayList< E >()); Map< K,V > synchHashMap = Collections.synchronizedMap(new HashMap< K,V >());
synchronized(synchHashMap) { Iterator< K > iter = synchHashMap.keySet().iterator(); while(iter.hashNext())...; }
public interface Callable< V > { V call() throws Exception; }类型参数是返回值的类型。例如,Callable< Integer >表示一个最终返回Integer对象的异步计算。
public interface Future< V > { V get() thros ...; V get(long timeout,TimeUnit unit) throwa...; void cancel(boolean mayInterupt); boolean isCancelled(); boolean isDone(); }第一个get方法的调用被阻塞,直到计算完成。若是在计算完成以前,第二个方法的调用超时,抛出一个TimeoutException异常。若是运行该计算的线程被中断,两个方法都将抛出InterruptedException。若是计算已经完成,那么get方法当即返回。
方法 | 描述 |
---|---|
newCachedThreadPool | 必要时建立新线程;空闲线程会被保留60秒 |
newFixedThreadPool | 该池包含固定数量的线程;空闲线程会一直被保留 |
newSingleThreadExecutor | 只有一个线程的“池”,该线程顺序执行每个提交的任务(相似于Swing事件分配线程) |
newScheduledThreadPool | 用于预约执行而构建的固定线程池,替代java.util.Timer |
newSingleThreadScheduleExecutor | 用于预约执行而构建的单线程“池” |
Future<?> submit(Runnable task) Future< T > submit(Runnable task,T result) Future< T > submit(Callable< T > task)该池会在方便的时候尽早执行提交的任务。调用submit时,会获得一个Future对象,可用来查询该任务的状态。
List<Callable< T >> tasks=...; List<Future< T >> results = executor.invokeAll(tasks); for (Future< T > result:results) processFurther(result.get());这个方法的缺点是若是第一个任务恰巧花去了不少时间,则可能按可得到的顺序保存起来更有实际意义。能够用ExecutorCompletionService来进行排列。
ExecutorCompletionService service = new ExecutorCompletionService(executor); for(Callable< T > task:tasks) service.submit(task); for (int i=0;i<task.size();i++) processFurther(service.task().get());
if(problemSize > threshold) solve problem direckly else { break problem into subproblems recursively solve each subproblem combine the results }
类 | 它能作什么 | 什么时候使用 |
---|---|---|
CyclicBarrier | 容许线程集等待直至其中预约数目的线程到达一个公共障栅(barrier),而后能够选择执行一个处理障栅的动做 | 当大量的线程须要在它们的结果可用以前完成时 |
CountDownLatch | 容许线程集等待直到计数器减为0 | 当一个或多个线程须要等待直到指定数目的事件发生 |
Exchanger | 容许两个线程在要交换的对象准备好时交换对象 | 当两个线程工做在同一个数据结构的两个实例上的时候,一个向实例添加数据而另外一个从实例清除数据 |
Semaphore | 容许线程集等待直到被容许继续运行为止 | 限制访问资源的线程总数。若是许可数是1,经常阻塞线程直到另外一个线程给出许可为止 |
SynchronoutQueue | 容许一个线程把对象交给另外一个线程 | 在没有显式同步的状况下,当两个线程准备好将一个对象从一个线程传递到另外一个时 |
将线程与 Swing 一块儿使用时,必须遵循两个简单的原则。java
制定第一条规则的理由易于理解。若是花不少时间在事件分配线程上,应用程序像“死了”同样,由于它不响应任何事件。特别是,事件分配线程应该永远不要进行 input/output 调用,这有可能会阻塞,而且永远不要调用 sleep 。(若是须要等待指定的时间,使用定时器事件。)
第二条规则在 Swing 编程中一般称为单一线程规则( single-thread rule )。
这两条规则看起来彼此冲突。假定要启动一个独立的线程运行一个耗时的任务。线程工做的时候,一般要灯芯用户界面中指示执行的进度。任务完成的时候,要再一次更新 GUI 界面。可是,不能从本身的线程接触 Swing 组件。例如,若是要更新进度条或标签文本,不能从线程中设置它的值。
要解决这一问题,在任何线程中,可使用两种有效的方法向事件队列添加任意的动做。例如,假定想在一个线程中周期性地更新标签来代表进度。不能够从本身的线程中调用 label.setText ,而应该使用 EventQueue 类的 invokeLater 方法和 invokeAndWait 方法使所调用的方法在事件分配线程中执行。
应该将 Swing 代码放置到实现 Runnable 接口的类的 run 方法中。而后,建立该类的一个对象,将其传递给静态的 invokeLater 或 invokeAndWait 方法。
当事件放入事件队列时, invokeLater 方法当即返回,而 run 方法被异步执行。 invokeAndWait 方法等待直到 run 方法确实被执行过为止。
有更新进度标签时, invokeLater 方法更适宜。用户更但愿让工做器线程有更快完成工做而不是获得更加精确的进度指示器。
这两种方法都是在事件分配线程中执行 run 方法。没有新的线程被建立。程序员