学习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>
使用Executor
Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。他们的关系为:
并发编程的一种编程方式是把任务拆分为一些列的小任务,即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
*/
}
未完待续