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