核心卷-多线程
2017-04-19 20:45:24 31 举报
AI智能生成
java核心卷(第九版)的第十四章,多线程。书中讲的有些没有示例代码,百度即可。看完第十四章,可对多线程有个初步入门。
作者其他创作
大纲/内容
14.1什么是线程
14.2 中断线程
每个线程都有一个boolean标志位来展示该线程是否被中断
isInterrupted() 可以检测线程是否被中断
当线程被阻塞,就无法检测中断状态了。
interrupt() : 像线程发送中断请求。线程的中断状态将被置为true。如果线程阻塞,那么,
interruptedException异常将被抛出。
interrupted() : 测试当前线程是否被中断。副作用——将当前线程的中断状态重置为false。
isInterrupted() : 测试线程是否被终止。(这一调用不改变线程的中断状态)
currentThread() : 返回当前执行线程的Thread对象
interruptedException异常将被抛出。
interrupted() : 测试当前线程是否被中断。副作用——将当前线程的中断状态重置为false。
isInterrupted() : 测试线程是否被终止。(这一调用不改变线程的中断状态)
currentThread() : 返回当前执行线程的Thread对象
14.3 线程状态
线程有6种状态:
New(新创建) Runnable(可运行)
Blocked(被阻塞) Waiting(等待)
Timed waiting(计时等待) Terminated(被终止)
New(新创建) Runnable(可运行)
Blocked(被阻塞) Waiting(等待)
Timed waiting(计时等待) Terminated(被终止)
14.3.1 创建新线程
当创建了一个新的线程,如 new Thread(), 该线程处于New状态
14.3.2 可运行线程
当调用start方法,线程就处于Runnable状态,处于Runnable的线程可能正在运行也可能没有运行。
14.3.3 被阻塞线程和等待线程
当线程获取一个内部的对象锁(不是java.util.concurrent库中的锁),且该锁被其它线程占用,则线程进入阻塞状态(Blocked)。
当线程等待另一个线程通知调度器一个条件时,则进入等待状态(Waiting)。(等待状态与阻塞状态是很不一样的)
有几个方法有一个超时参数。调用他们导致线程进入计时等待(timed waiting)
14.3.4 被终止的线程
线程被终止:
1,因为run方法正常退出而自然死亡的
2,因为一个没有捕获的异常终止了run方法而意外死亡
1,因为run方法正常退出而自然死亡的
2,因为一个没有捕获的异常终止了run方法而意外死亡
14.4 线程属性
14.4.1 线程优先级
可以用setPriority方法设置线程优先级在MIN_PRIORITY(1) 与 MAX_PRIORITY(10)之间的任何值。 NORM_PRIORITY被定义为5
当调用start方法,线程就处于Runnable状态,处于Runnable的线程可能正在运行也可能没有运行。
优先级并不靠谱,例如在Linux的虚拟机上,所有的线程具有相同的优先级
优先级并不靠谱,例如在Linux的虚拟机上,所有的线程具有相同的优先级
yield() 导致当前执行线程处于让步状态。如果有其他的可运行线程具有至少与此线程同样高的优先级,那么这些线程接下来将会被调度。
14.4.2 守护线程
t.setDaemon(true)将线程转换为守护线程,必须在t.start()方法调用前设置。线程start()之后,不可以改变线程为守护线程了。
当只剩下守护线程时,虚拟机就退出了。也就是说,JVM判断程序是否运行完成的标志是,前台线程都执行完成。
14.4.3 未捕获异常处理器
线程的run方法没法抛出checked异常,runtime异常则会导致线程终止,然后死亡
当线程发生异常时,可以不需要显示的catch语句,应为在线程死亡之前,会将异常发送到一个未捕获异常处理器( uncaugthException() )。
该处理器需要实现Thread.UncaughtExceptionHandler接口且实现
uncaughtException(Threead t, Throwable e)方法。
该处理器需要实现Thread.UncaughtExceptionHandler接口且实现
uncaughtException(Threead t, Throwable e)方法。
为某一线程安装处理器:
t.setUncaughtExceptionHandler();
为所有线程安装默认处理器:
Thread.setDefaultUncaughtExceptionHandler();
如果不安装处理器,则此时的处理器就是ThreadGroup对象
t.setUncaughtExceptionHandler();
为所有线程安装默认处理器:
Thread.setDefaultUncaughtExceptionHandler();
如果不安装处理器,则此时的处理器就是ThreadGroup对象
14.5 同步
14.5.3 锁对象
用ReentrantLock保护代码块的基本结构如下:
myLock.lock(); //a ReentrantLock object
try {
...
} finally {
myLock.unlock();
}
当其他线程试图进入代码块时,调用myLock.lock()方法,被阻塞,直到第一个线程释放锁对象
myLock.lock(); //a ReentrantLock object
try {
...
} finally {
myLock.unlock();
}
当其他线程试图进入代码块时,调用myLock.lock()方法,被阻塞,直到第一个线程释放锁对象
不可以将锁放在带资源的try 括号中,try-with-resources需要资源必须实现AutoCloseable或者Closeable接口,必须试下close()方法。
yield() 导致当前执行线程处于让步状态。如果有其他的可运行线程具有至少与此线程同样高的优先级,那么这些线程接下来将会被调度。
每个实例对象都要最有自己的锁对象。如果多个线程试图访问同一个实例对象,那么锁将起到作用,但是如果多个线程访问不同的实例对象,每个线程得到的是不同的锁,每个线程都不会发生阻塞。
锁是可以重入的,锁持有一个持有计数(hold count)来跟踪锁的释放状态。
ReentrantLock(boolean fair) :将创建一个带有公平策略的锁,公平锁偏爱等待时间最长的线程。但是,这样对导致性能大大的降低。
14.5.4 条件对象
等待获得锁的线程和调用await方法的线程存在本质上不同
1,等待获得锁的线程一旦获得锁,就立马解除了阻塞状态。
2,调用await方法的线程,当锁可用时,该线程不能马上解除阻塞状态。需要另一个线程调用同一条件上的signalAll方法时为止。
1,等待获得锁的线程一旦获得锁,就立马解除了阻塞状态。
2,调用await方法的线程,当锁可用时,该线程不能马上解除阻塞状态。需要另一个线程调用同一条件上的signalAll方法时为止。
当一个线程调用await时,它没有办法重新激活自身。最终需要某个其它线程调用signalAll方法。如果没有其它线程来重新激活等待的线程,它就永远不会执行了。
就发生了死锁(deadlock)
就发生了死锁(deadlock)
signalAll方法不会立即激活一个等待线程。它仅仅解除等待线程的阻塞,等待当前线程退出同步方法,通过竞争实现对对象的访问。
signal() 则是随机解除等待集中某个线程的阻塞状态。
当一个线程拥有某个条件的锁时,它仅仅可以在该条件上调用await、signalAll、siganl方法
signal() 则是随机解除等待集中某个线程的阻塞状态。
当一个线程拥有某个条件的锁时,它仅仅可以在该条件上调用await、signalAll、siganl方法
Condition sufficientFunds = bankLock.newCondition ( );
14.5.5 synchronized关键字
从1.0开始,java中的每一个对象都有一个内部锁。
public synchronized void method ()
{
method body ...
}
等价于
public void method()
{
this.intrinsicLock.lock();
try
{
method body ...
}
finally { this.intrinsicLock.unLock() }
}
public synchronized void method ()
{
method body ...
}
等价于
public void method()
{
this.intrinsicLock.lock();
try
{
method body ...
}
finally { this.intrinsicLock.unLock() }
}
内部对象只有一个相关条件。wait方法添加一个线程到等待集中,notifyAll/notify方法解除等待线程的阻塞状态。
wait/notifyAll/notify 等价于 intrinsicCondition.await();
intrinsicCondition.signalAll();signal()
wait/notifyAll/notify 等价于 intrinsicCondition.await();
intrinsicCondition.signalAll();signal()
如果将静态方法声明为synchronized,则该方法获得的内部锁是类对象。如果该静态同步方法被调用,则class对象将被锁住。所以其它线程无法调用该方法或该类的其它静态同步方法
14.5.6 同步阻塞
当线程进入如下形式的阻塞:
synchronized(obj) {
critical section
}
于是就获得了obj的锁
synchronized(obj) {
critical section
}
于是就获得了obj的锁
14.5.7 监视器概念
14.5.8 Volatile域
如果声明一个成员变量为volatile,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的。
volatile变量不能提供原子性
14.5.10 原子性
如果对共享变量除了赋值之外并不完成其他操作,那么可以将这些共享变量声明为volatile
AtomicInteger类提供了incrementAndGet和decrementAndGet,这两个操作是原子性的。
14.5.12 线程局部变量
public static final ThreadLocak<SimpleDateFormat> dateFormat =
new ThreadLocal<SImpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
new ThreadLocal<SImpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
get() : 得到这个线程的当前值。如果是首次调用,则会执行initalize来初始化这个值
initialize() : 提供初始值,默认返回null
set():为这个线程设置一个新值
remove():删除这个线程的对应值
initialize() : 提供初始值,默认返回null
set():为这个线程设置一个新值
remove():删除这个线程的对应值
14.5.13 锁测试与超时
tryLock() : 尝试获得锁而没有发生阻塞,如果成功返回真。这个方法会抢夺可用的锁,即使该锁有公平加锁策略,即便
14.5.14 读/写锁
如果很多线程从一个数据结构中读取数据而很少修改其中的数据,那么应该允许读者线程共享访问,写线程依然必须是互斥访问的。
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private Lock readLock = rwl.readLock();
(得到一个可以被对多个多操作共用的读锁,但会排斥所有的写操作)
private Lock writeLock = rwl.writeLock();
(得到一个写锁,排斥所有其他的读操作和写方法)
private Lock readLock = rwl.readLock();
(得到一个可以被对多个多操作共用的读锁,但会排斥所有的写操作)
private Lock writeLock = rwl.writeLock();
(得到一个写锁,排斥所有其他的读操作和写方法)
14.6 阻塞队列
当向队列添加元素二队列已经满了,或者从空队列移除元素时,阻塞队列(blocking queue)导致线程阻塞
阻塞队列方法:
add : 添加一个元素; 如果队列满了,则抛出IllegalStateException异常
offer: 添加一个元素 ; 如果队列满,返回false
put : 添加一个元素; 如果队列满,则阻塞
element:返回队列的头元素;如果队列空,抛出NoSuchElementException异常
peek:返回队列的头元素;如果队列空,则返回null
poll:移出并返回队列的头元素;如果队列为空,则返回null
remove:移除并返回头元素;如果队列为空,则抛出NoSuchElementException异常
take:移出并返回头元素;如果队列为空,则阻塞
add : 添加一个元素; 如果队列满了,则抛出IllegalStateException异常
offer: 添加一个元素 ; 如果队列满,返回false
put : 添加一个元素; 如果队列满,则阻塞
element:返回队列的头元素;如果队列空,抛出NoSuchElementException异常
peek:返回队列的头元素;如果队列空,则返回null
poll:移出并返回队列的头元素;如果队列为空,则返回null
remove:移除并返回头元素;如果队列为空,则抛出NoSuchElementException异常
take:移出并返回头元素;如果队列为空,则阻塞
默认情况下,LinkedBlockingQueue的容量是没有上边界的。
LinkedBlockingDeque是一个双端的LinkedBlockingQueue版本。
ArrayBlockingQueue在构造是需要制定容量
PriorityBlockingQueue是一个带优先级的队列,元素按照优先级顺序被移除
LinkedBlockingDeque是一个双端的LinkedBlockingQueue版本。
ArrayBlockingQueue在构造是需要制定容量
PriorityBlockingQueue是一个带优先级的队列,元素按照优先级顺序被移除
14.7 线程安全的集合
14.8 Callable与Future
Callable接口只有一个方法call
public interface Future<V> {
V call() throws Exception
}
public interface Future<V> {
V get() throws ...; //被阻塞,直到其他线程计算完成
V get(long timeout, TimeUnit unit) throws ...; //在计算完成之前,调用超时,
抛出TimeoutException异常
void cancel(boolean mayInterrupt); //计算没开始,被取消且不再开始,如果处于运行 中,那么如果mayInterrupt参数为true,它就被中断。
boolean isCancelled();
boolean isDone();//如果计算在进行,idDone返回false;如果完成,返回true
}
public interface Future<V> {
V call() throws Exception
}
public interface Future<V> {
V get() throws ...; //被阻塞,直到其他线程计算完成
V get(long timeout, TimeUnit unit) throws ...; //在计算完成之前,调用超时,
抛出TimeoutException异常
void cancel(boolean mayInterrupt); //计算没开始,被取消且不再开始,如果处于运行 中,那么如果mayInterrupt参数为true,它就被中断。
boolean isCancelled();
boolean isDone();//如果计算在进行,idDone返回false;如果完成,返回true
}
14.9 执行器
newCachedThreadPool : 必要时创建线程,空闲线程会被保留60秒
newFixedThreadPool : 该池包含固定数量的线程,空闲线程会一直被保留
newSingleThreadExecutor : 只有一个线程的池,该线程顺序执行每一个提交的任务
newScheduledThreadPool : 用于预定执行而构建的固定线程池
newSingleThreadScheduledExecutor : 用于预定执行而构建的单线程池
newFixedThreadPool : 该池包含固定数量的线程,空闲线程会一直被保留
newSingleThreadExecutor : 只有一个线程的池,该线程顺序执行每一个提交的任务
newScheduledThreadPool : 用于预定执行而构建的固定线程池
newSingleThreadScheduledExecutor : 用于预定执行而构建的单线程池
14.9.1 线程池
Future<?> submit(Runnable task) : get方法返回null
Future<T> submit(Runnable task, T result) : 返回指定的result对象
Future<T> submit(Callable<T> task) : 返回callable的结果
Future<T> submit(Runnable task, T result) : 返回指定的result对象
Future<T> submit(Callable<T> task) : 返回callable的结果
当线程池完成时,调用shutdown。被关闭的执行器不在接受新的任务。当所有的任务完成以后,线程池中的线程死亡。
shutdownNow : 该池取消尚未开始的所有任务并试图中断正在运行的线程。
shutdownNow : 该池取消尚未开始的所有任务并试图中断正在运行的线程。
14.9.2 预定执行
ScheduledExecutorService接口具有为预定执行或重复执行任务而设计的方法。
ScheduledFuture<V> schedule(Callable<V> task, long time, TimeUnit unit)
ScheduledFuture<?> schedule(Runnable task, long time, TimeUnit unit)
预定在指定的时间后执行任务。
ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit)
预定在初始的延迟结束后,周期性地运行给定的任务,周期长度是period
ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit)
预定在初始的延迟结束后周期性地执行给定的任务,在一次调用完成和下一次调用开始之间有长度为delay的延迟
ScheduledFuture<?> schedule(Runnable task, long time, TimeUnit unit)
预定在指定的时间后执行任务。
ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit)
预定在初始的延迟结束后,周期性地运行给定的任务,周期长度是period
ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit)
预定在初始的延迟结束后周期性地执行给定的任务,在一次调用完成和下一次调用开始之间有长度为delay的延迟
14.9.3 控制任务组
T invokeAny(Collection<Callable<T>> tasks)
T invokeAny(Collection<Callable<T>> tasks, long timeout, TimeUnit unit)
执行给定的任务,返回其中一个任务的结果。如果超时,抛出一个TimeoutException
List<Future<T>> invokeAll(Collection<Callable<T>> tasks)
List<Future<T>> invokeAll(Collection<Callable<T>> taks, long timeout, TimeUnit unit)
执行给定的任务,返回所有的任务结果。
(结果按照Callable顺序排列)
T invokeAny(Collection<Callable<T>> tasks, long timeout, TimeUnit unit)
执行给定的任务,返回其中一个任务的结果。如果超时,抛出一个TimeoutException
List<Future<T>> invokeAll(Collection<Callable<T>> tasks)
List<Future<T>> invokeAll(Collection<Callable<T>> taks, long timeout, TimeUnit unit)
执行给定的任务,返回所有的任务结果。
(结果按照Callable顺序排列)
ExecutorCompletionService(Executor e)
构建一个执行器完成服务来收集给定执行器的结果
Future<T> submit(Callable<T> task)
Future<T> submit(Runnable task, T result)
提交一个任务给底层执行器
Future<T> take()
移除下一个已完成的结果,如果没有任何已完成的结果可用则阻塞。
Future<T> pool()
Future<T> pool(long time, TimeUnit unit)
移除下一个已完成的结果,如果没有任何已完成结果可用则返回null。第二个方法将等待给定的时间。
(结果按照可获得的顺序排列)
构建一个执行器完成服务来收集给定执行器的结果
Future<T> submit(Callable<T> task)
Future<T> submit(Runnable task, T result)
提交一个任务给底层执行器
Future<T> take()
移除下一个已完成的结果,如果没有任何已完成的结果可用则阻塞。
Future<T> pool()
Future<T> pool(long time, TimeUnit unit)
移除下一个已完成的结果,如果没有任何已完成结果可用则返回null。第二个方法将等待给定的时间。
(结果按照可获得的顺序排列)
14.9.4 Fork-Join框架
扩展RecursiveTask<T>, 产生一个类型为T的结果
扩展RecursiveAction的类,不产生结果
(重写computer方法)
扩展RecursiveAction的类,不产生结果
(重写computer方法)
14.10 同步器
栅栏
CyclicBarrier
CyclicBarrier barrier = new CyclicBarrier(num);
public void run() {
...;
barrier.await();
}
CyclicBarrier
CyclicBarrier barrier = new CyclicBarrier(num);
public void run() {
...;
barrier.await();
}
倒计时门栓
CountDownLatch
CountDownLatch latch = new CountDownLatch(num);
public void run() {
....;
latch.countDown();
}
latch.await();
CountDownLatch
CountDownLatch latch = new CountDownLatch(num);
public void run() {
....;
latch.countDown();
}
latch.await();
交换器
Exchanger
Exchanger exchanger = new Exchanger();
DataProducer :
exchanger.exchange(data);
DataConsumer :
exchanger.exchange(data);
Exchanger
Exchanger exchanger = new Exchanger();
DataProducer :
exchanger.exchange(data);
DataConsumer :
exchanger.exchange(data);
信号量(许可证)
Semaphore
Semaphore semaphore = new Semaphore(num);
public void run {
...;
semaphore.acquire();
...;
semaphore.release();
}
Semaphore
Semaphore semaphore = new Semaphore(num);
public void run {
...;
semaphore.acquire();
...;
semaphore.release();
}
同步队列
SynchronousQueue
SynchronousQueue queue = new SynchronousQueue();
DataProducer :
queue.put(); //block
DataConsumer :
queue.take(); //block
可参考阻塞队列的实现
SynchronousQueue
SynchronousQueue queue = new SynchronousQueue();
DataProducer :
queue.put(); //block
DataConsumer :
queue.take(); //block
可参考阻塞队列的实现
0 条评论
下一页