本章内容:
* 什么是线程
* 中断线程
* 线程状态
* 线程属性
* 同步
* 阻塞队列
* 线程安全的集合
* 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 一起使用时,必须遵循两个简单的原则。
制定第一条规则的理由易于理解。如果花很多时间在事件分配线程上,应用程序像“死了”一样,因为它不响应任何事件。特别是,事件分配线程应该永远不要进行 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 方法。没有新的线程被创建。
原文:https://www.cnblogs.com/zhangmiao14/p/9992471.html