学习JAVA编程思想第二十一章-并发
编写火箭10秒倒数发射线程类,该类实现runnable接口。
技术点:
1、Thread.yeid() 和 Thread.sleep() 的区别?
Thread.yeid() 线程的调度(让步),暂停当前正在执行的线程对象,并执行其他线程。(注意:这里的其他也包含当前线程)
Thread.sleep() 使当前线程暂停millis所指定的毫秒,转到执行其它线程。
package com.javanet.thread; /** * 多线程 * 定时任务 火箭倒数10秒发射实例 */ public class ListOff implements Runnable { //倒数计时 protected int countDown = 10; //线程任务计数器 默认值0 private static int taskCount = 0; //线程任务 private final int id = taskCount++; public ListOff() { } public ListOff(int countDown) { this.countDown = countDown; } public String status() { return "#"+id+"("+(countDown > 0 ? countDown : "ListOff!")+"), "; } @Override public void run() { while (countDown-- > 0) { System.out.print(status()); Thread.yield(); } } }执行定时任务类
/** * 将runnable对象变为工作任务的传统方式: * 提交给Thread构造器 */ @Test public void test1() { Thread t = new Thread(new ListOff()); t.start(); System.out.println("Waiting for ListOff!"); } /** * 添加5个任务启动工作任务 */ @Test public void test2() { for (int i = 0; i < 5; i++) { new Thread(new ListOff()).start(); } System.out.println("Waiting for ListOff!"); }执行结果:
//执行test1()结果 Waiting for ListOff! #0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(ListOff!), //执行test2()结果 Waiting for ListOff! #3(9), #1(9), #4(9), #2(9), #0(9), #3(8), #1(8), #4(8), #2(8), #0(8), #3(7), #4(7), #1(7), #2(7), #0(7), #4(6), #1(6), #3(6), #2(6), #0(6), #4(5), #1(5), #3(5), #2(5), #0(5), #4(4), #1(4), #3(4), #2(4), #0(4), #4(3), #1(3), #3(3), #2(3), #0(3), #4(2), #1(2), #3(2), #2(2), #0(2), #4(1), #1(1), #3(1), #2(1), #0(1), #1(ListOff!), #3(ListOff!), #4(ListOff!), #2(ListOff!),
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">输出说明不同任务的执行在线程被换进换出时混在一起。这种交换是由线程调度器自动控制的。如果在你的机器上有多个处理器,线程调度器将会在这些处理器之间默默的分发线程。</span>
并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,Executor.execute(Runnalbe) 。Executor在执行时使用内部的线程池完成操作。
Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。
public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目线程的线程池。/** * 创建固定数量的线程池 */ @Test public void test3() { ExecutorService es = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { es.execute(new ListOff()); } es.shutdown(); }
#2(9), #1(9), #4(9), #3(9), #0(9), #2(8), #1(8), #3(8), #4(8), #0(8), #2(7), #0(7), #4(7), #3(7), #1(7), #0(6), #4(6), #3(6), #2(6), #1(6), #0(5), #4(5), #3(5), #2(5), #1(5), #0(4), #4(4), #3(4), #2(4), #0(3), #1(4), #4(3), #3(3), #2(3), #0(2), #1(3), #4(2), #0(1), #2(2), #3(2), #1(2), #4(1), #0(ListOff!), #2(1), #3(1), #1(1), #4(ListOff!), #2(ListOff!), #3(ListOff!), #1(ListOff!),
public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
有了FixedThreadPool,你就可以一次性预先执行代价高昂的线程分配,因而也就可以限制线程的数量了。这可以节省时间,因为你不用为每个任务都固定创建线程的开销
@Test public void test4() { ExecutorService es = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { es.execute(new ListOff()); } es.shutdown(); }
#3(9), #0(9), #1(9), #4(9), #2(9), #3(8), #0(8), #1(8), #4(8), #2(8), #3(7), #0(7), #1(7), #4(7), #2(7), #3(6), #0(6), #1(6), #4(6), #2(6), #3(5), #0(5), #1(5), #4(5), #2(5), #3(4), #0(4), #1(4), #4(4), #2(4), #3(3), #0(3), #4(3), #1(3), #2(3), #3(2), #0(2), #4(2), #1(2), #2(2), #3(1), #0(1), #4(1), #1(1), #2(1), #3(ListOff!), #0(ListOff!), #4(ListOff!), #1(ListOff!), #2(ListOff!),
CachedThreadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是合理的Executor首选。只有当这种方式会引发问题时候,你才需要切换到FixedThreadPool。
public static ExecutorService newSingleThreadExecutor()
创建一个单线程化的Executor。
1、SingleThreadExecutor就像线程为1的FixedThreadPool
2、如果向SingleThreadExecutor提交多个任务,这些任务将排队。从输出结果可以看到,任务按照提交顺序被执行。
3、SingleThreadExecutor会序列化所有提交的任务,并维护自己(隐藏)的悬挂任务队列。
4、SingleThreadExecutor可以确保任何线程中都只有唯一的任务在运行。(多个线程使用同一文件系统时,可以用SingleThreadExecutor来保持同步)/** * SingleThreadExecutor */ @Test public void test5() { ExecutorService es = Executors.newSingleThreadExecutor(); for (int i = 0; i < 5; i++) { es.execute(new ListOff()); } es.shutdown(); }
#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(ListOff!), #1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(ListOff!), #2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(ListOff!), #3(9), #3(8), #3(7), #3(6), #3(5), #3(4), #3(3), #3(2), #3(1), #3(ListOff!), #4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(ListOff!),
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
/** * scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnitunit) * * 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期; * 也就是将在 initialDelay 后开始执行, * 然后在initialDelay+period 后执行, * 接着在 initialDelay + 2 * period 后执行,依此类推。 * * 下面例子: * 创建beeper定时任务,0秒开始执行,每隔10秒再执行一次,直到1*60秒后取消定时任务 */ private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); public void beepForAnHour() { final Runnable beeper = new Runnable() { public void run() { System.out.println("beep"); } }; final ScheduledFuture<?> beeperHandle = scheduler.<span style="color:#ff0000;">scheduleAtFixedRate</span>(beeper, 0, 10, TimeUnit.SECONDS); scheduler.schedule(new Runnable() { public void run() { beeperHandle.cancel(true); } }, 1 * 60, TimeUnit.SECONDS); } public static void main(String[] args) { new TestThread().beepForAnHour(); }
beep beep beep beep beep beep beep
final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); /** * 创建并执行在给定延迟后启用的一次性操作。 */ exec.<span style="color:#ff0000;">schedule</span>(new Runnable() { public void run() { System.out.println("The thread can only run once!"); } },5000,TimeUnit.MILLISECONDS);
/** * 创建并执行一个在给定初始延迟后首次启用的定期操作,<br/> * 随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。 */ exec.<span style="color:#ff0000;">scheduleWithFixedDelay</span>(new Runnable() { public void run() { System.out.println("scheduleWithFixedDelay:begin,"+format.format(new Date())); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("scheduleWithFixedDelay:end,"+format.format(new Date())); } },1000,5000,TimeUnit.MILLISECONDS);
注意:
1、开始执行后就触发异常,next周期将不会运行。解决办法是抛出所有可能的异常,当被拦截了,next周期继续运行。
2、scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 和scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) 的区别很明显,主要区别在第三个参数上面。
它们不同的是前者以固定频率执行,后者以相对固定频率执行。
前者表示 程序启动initialDelay秒后执行command,period秒后再次执行command,不管command任务有没有执行完成。
后者表示 程序启动initialDelay秒后执行command,当command执行完毕,等待delay秒后,再次执行。
从任务中产生返回值
package com.javanet.thread; import java.util.concurrent.Callable; /** * 带返回值的多线程接口 */ public class TaskWithResult implements Callable<String> { private int id; public TaskWithResult(int id) { this.id = id; } @Override public String call() throws Exception { return "result of taskWithResult: " + id; } }
package com.javanet.thread; import java.util.ArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CallableDemo { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); ArrayList<Future<String>> results = new ArrayList<Future<String>>(); for (int i = 0; i < 10; i++) { Future<String> submit = exec.submit(new TaskWithResult(i)); if (submit.isDone()) { results.add(submit); } } for (Future<String> future : results) { try { System.out.println(future.get()); } catch (InterruptedException e) { e.printStackTrace(); return; } catch (ExecutionException e) { e.printStackTrace(); } finally { exec.shutdown(); } } } }执行结果
result of taskWithResult: 0 result of taskWithResult: 1 result of taskWithResult: 2 result of taskWithResult: 3 result of taskWithResult: 4 result of taskWithResult: 5 result of taskWithResult: 6 result of taskWithResult: 7 result of taskWithResult: 8 result of taskWithResult: 9休眠 sleep
package com.javanet.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class SleepTask extends ListOff { @Override public void run() { try { while (countDown-- > 0) { System.out.print(status()); TimeUnit.MILLISECONDS.sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { exec.execute(new SleepTask()); } exec.shutdown(); } }执行结果
#3(9), #2(9), #1(9), #4(9), #0(9), #0(8), #4(8), #1(8), #3(8), #2(8), #2(7), #3(7), #0(7), #1(7), #4(7), #3(6), #1(6), #4(6), #0(6), #2(6), #0(5), #1(5), #2(5), #4(5), #3(5), #0(4), #4(4), #1(4), #2(4), #3(4), #3(3), #0(3), #2(3), #1(3), #4(3), #1(2), #3(2), #4(2), #2(2), #0(2), #2(1), #1(1), #4(1), #0(1), #3(1), #3(ListOff!), #1(ListOff!), #0(ListOff!), #2(ListOff!), #4(ListOff!),
优先级
package com.javanet.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 优先级 */ public class SimplePriorities implements Runnable { private int countDown = 5; private volatile double d; private int priority; public SimplePriorities(int priority) { this.priority = priority; } public String toString() { return Thread.currentThread() + ": " + countDown; } @Override public void run() { //设置当前线程的优先级 Thread.currentThread().setPriority(priority); while (true) { for (int i = 0; i < 100000; i++) { d += (Math.PI + Math.E) / (double) i; if (i % 1000 == 0) { Thread.yield(); } System.out.println(this); if (--countDown == 0) return; } } } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { exec.execute(new SimplePriorities(Thread.MIN_PRIORITY)); } exec.execute(new SimplePriorities(Thread.MAX_PRIORITY)); exec.shutdown(); } }执行结果
Thread[pool-1-thread-2,1,main]: 5 Thread[pool-1-thread-5,1,main]: 5 Thread[pool-1-thread-5,1,main]: 4 Thread[pool-1-thread-2,1,main]: 4 Thread[pool-1-thread-3,1,main]: 5 Thread[pool-1-thread-4,1,main]: 5 Thread[pool-1-thread-6,10,main]: 5 Thread[pool-1-thread-1,1,main]: 5 Thread[pool-1-thread-6,10,main]: 4 Thread[pool-1-thread-4,1,main]: 4 Thread[pool-1-thread-3,1,main]: 4 Thread[pool-1-thread-2,1,main]: 3 Thread[pool-1-thread-5,1,main]: 3 Thread[pool-1-thread-2,1,main]: 2 Thread[pool-1-thread-3,1,main]: 3 Thread[pool-1-thread-4,1,main]: 3 Thread[pool-1-thread-6,10,main]: 3 Thread[pool-1-thread-1,1,main]: 4 Thread[pool-1-thread-6,10,main]: 2 Thread[pool-1-thread-4,1,main]: 2 Thread[pool-1-thread-3,1,main]: 2 Thread[pool-1-thread-2,1,main]: 1 Thread[pool-1-thread-5,1,main]: 2 Thread[pool-1-thread-5,1,main]: 1 Thread[pool-1-thread-3,1,main]: 1 Thread[pool-1-thread-4,1,main]: 1 Thread[pool-1-thread-6,10,main]: 1 Thread[pool-1-thread-1,1,main]: 3 Thread[pool-1-thread-1,1,main]: 2 Thread[pool-1-thread-1,1,main]: 1让步yied()
后台线程
所谓后台(daemon)线程,也叫守护线程。是指在程序运行的时候再后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来,只要有任何非后台线程还在运行,程序就不会终止。比如,执行main()就是一个非后台线程。
在线程启动之前调用setDaemon()方法,就能把线程设为后台线程
在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
只要当前JVM实例中尚存在任何一个用户线程没有结束,守护线程就全部工作;只有当最后一个用户线程结束时,守护线程随着JVM一同结束工作。
package com.javanet.thread; import java.util.concurrent.TimeUnit; /** * 后台线程 */ public class SimpleDaemons implements Runnable{ @Override public void run() { try { while (true) { TimeUnit.MILLISECONDS.sleep(100); System.out.println(Thread.currentThread() + "" + this); } } catch (Exception e) { System.out.println("sleep() interrupted"); } } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 10; i++) { Thread daemon = new Thread(new SimpleDaemons()); daemon.setDaemon(true); daemon.start(); } System.out.println("All daemons started"); TimeUnit.MILLISECONDS.sleep(100); } }编码的变体
1、继承Thread类方式
package com.javanet.thread; /** * 继承thread */ public class SimpleThread extends Thread { private int countDown = 5; private static int threadCount = 0; public SimpleThread() { start(); } public String toString() { return "#" + getName() + "(" + countDown +"), "; } @Override public void run() { while (true) { System.out.print(this); if (--countDown == 0) { return; } } } public static void main(String[] args) { for (int i = 0; i < 5; i++) { new SimpleThread(); } } //执行结果:#Thread-0(5), #Thread-1(5), #Thread-2(5), #Thread-0(4), #Thread-2(4), #Thread-1(4), #Thread-4(5), #Thread-1(3), #Thread-4(4), #Thread-4(3), #Thread-2(3), #Thread-3(5), #Thread-0(3), #Thread-3(4), #Thread-2(2), #Thread-4(2), #Thread-1(2), #Thread-4(1), #Thread-2(1), #Thread-3(3), #Thread-0(2), #Thread-3(2), #Thread-3(1), #Thread-1(1), #Thread-0(1), }
2、资管理runnable
package com.javanet.thread; /** * 自管理 */ public class SelfManaged implements Runnable { private int countDown = 5; private Thread t = new Thread(this); public SelfManaged() { t.start(); } public String toString() { return Thread.currentThread().getName() + "(" + countDown +"), "; } @Override public void run() { while (true) { System.out.print(this); if (--countDown == 0) { return; } } } public static void main(String[] args) { for (int i = 0; i < 5; i++) { new SelfManaged(); } } }
3、内部类方式将线程代码隐藏在类中
package com.javanet.thread; /** * 内部类 */ public class InnerThread1 { private int countDown = 5; private Inner inner; public InnerThread1(String name) { inner = new Inner(name); } class Inner extends Thread { public Inner(String name) { super(name); start(); } @Override public void run() { try { while (true) { System.out.println(this); if (--countDown == 0) { return; } } } catch (Exception e) { } } @Override public String toString() { return getName()+": " + countDown; } } public static void main(String[] args) { new InnerThread1("小明"); } /** * 执行结果 * 小明: 5 小明: 4 小明: 3 小明: 2 小明: 1 */ }