今天看了这一篇
Java编程思想-java中的并发(一)
记一下学习笔记。
这篇文章中前两节关于基本线程的创建之前在教材中有更好的例子和解释,所以直接跳到第三节:
使用Executor
JAVA SE5的java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程。Executor在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务。Executor允许你管理异步任务的执行,而无需显式的管理线程的生命周期。Executor在Java SE5/6中是启动任务的优选方法。
贴上代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* \* Created with IntelliJ IDEA.
* \* User: voicebeer
* \* Date: 2017/3/26
* \* Time: 下午3:04
* \* Description:This for tests
*/
public class Test {
private static int taskCount = 0;
private final int id = taskCount++;
public static class LiftOff implements Runnable {
protected int countDown = 5;
private int taskCount = 0;
private final int id = taskCount++;
public LiftOff() {
}
public LiftOff(int countDown) {
this.countDown = countDown;
}
public String status() {
return "#" + id + "(" + (countDown > 0 ? countDown : "Liftoff!") + ").";
}
@Override
public void run() {
while (countDown-- > 0) {
System.out.println(status());
Thread.yield();
}
}
}
public static void main(String[] args) {
ExecutorService exec = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++) {
exec.execute(new Main.LiftOff());
}
exec.shutdown();
}
}
可以发现:
JAVA SE5的java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程。Executor在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务。
FixedThreadPool使用了有限的线程集来执行所提交的任务
在这里我首先用FixedThreadPool预先分配出两个线程用作使用,但是在下面的execute中要执行五次,所以就是像结果里一样,一次用两个得来执行。原文中也提到:
CachedThreadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在他回收旧线程时停止创建新线程,因此它是合理的Executor的首选。只有当这种方式会引发问题时,你才需要切换到FixedThreadPool。
所以相比之下还是CachedThreadPool用途更广点吧,需要定制的时候才会用到FixedThreadPool。
如果你希望任务在完成时能够返回一个值,那么可以实现Callable接口而不是Runnable接口。在Java SE5中引入的Callable是一种具有类型参数的泛型,它的类型参数表示的是从方法call()(而不是run())中返回的值,并且必须使用ExecutorService.submit()方法调用他.
老样子先上代码:
import java.util.ArrayList;
import java.util.concurrent.*;
/**
* \* Created with IntelliJ IDEA.
* \* User: voicebeer
* \* Date: 2017/3/26
* \* Time: 下午3:04
* \* Description:This for tests
*/
public class Test {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
ArrayList> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(exec.submit(new TaskWithResult(i)));
}
for (Future fs : results) {
try {
//if(fs.isDone()) {
System.out.println(fs.get());
//}
} catch (InterruptedException e) {
System.out.println(e);
return;
} catch (ExecutionException e) {
System.out.println(e);
} finally {
exec.shutdown();
}
}
}
}
class TaskWithResult implements Callable {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
public String call() {
return "result of TaskWithResult " + id;
}
}
我在代码里注释了一段
//if(fs.isDone()) {
System.out.println(fs.get());
//}
这边的isDone就是Callable相对于Runnable的特点,在任务完成时,会有一个返回值,引用原文的话:
你可以用isDone()方法来查询Future是否已经完成。当任务完成时,它具有一个结果,你可以调用get()方法来获取该结果。你也可以不用isDone()进行检查就直接调用get(),这种情况下,get()将阻塞,直至结果准备就绪。你还可以在试图调用get()来获取结果之前,先调用具有超时的get(),或者调用isDone()来查看任务是否完成。
所谓后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。比如,执行main()的就是一个非后台线程。
代码:
import java.util.concurrent.TimeUnit;
/**
* \* Created with IntelliJ IDEA.
* \* User: voicebeer
* \* Date: 2017/3/26
* \* Time: 下午3:04
* \* Description:This for tests
*/
public class Test {
public static class SimpleDaemon implements Runnable {
@Override
public void run() {
try {
while (true) {
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread() + " " + this);
}
} catch (InterruptedException e) {
System.out.println("sleep() interrupted");
}
}
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
Thread daemon = new Thread(new SimpleDaemon());
daemon.setDaemon(true);
daemon.start();
}
System.out.println("All daemons started");
TimeUnit.MILLISECONDS.sleep(175);
}
}
在上面的代码块里如果把main函数里的
TimeUnit.MILLISECONDS.sleep(175);
设置成比如98ms(之所以不用99是由于存在小数点等问题,其实差1的话并不是表面上的差1),也就是小于SimpleDaemon中的100ms的话,结果就是:
显而易见,没有后台线程了,因为是非后台线程main()先于SimpleDaemon结束,所以杀死了所有后台线程。
一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行。如果某个线程在另一个线程t上调用t.join(),此线程将被挂起,直到目标线程t结束才恢复(即t.isAlive()返回为假)。
对join()方法的调用可以被中断,做法是在调用线程上调用interrupt()方法,这时需要用到try-catch子句。
代码:
public class Joining {
public static void main(String[] args) {
Sleeper
sleepy = new Sleeper("Sleepy", 1500),
grumpy = new Sleeper("grumpy", 1500);
Joiner
dopey = new Joiner("Dopey", sleepy),
doc = new Joiner("Doc", grumpy);
grumpy.interrupt();
}
}
class Sleeper extends Thread {
private int duration;
public Sleeper(String name, int sleepTime) {
super(name);
duration = sleepTime;
start();
}
public void run() {
try {
sleep(duration);
} catch (InterruptedException e) {
System.out.println(getName() + " was interrupted. " + "interrupted(): " + isInterrupted());
}
System.out.println(getName() + " has awakened");
}
}
class Joiner extends Thread {
private Sleeper sleeper;
public Joiner(String name, Sleeper sleeper) {
super(name);
this.sleeper = sleeper;
start();
}
public void run() {
try {
sleeper.join();
} catch (InterruptedException e) {
System.out.println("interrupted.");
}
System.out.println(getName() + " join completed.");
}
}
结果:
可以看到,首先是按照主函数里的顺序,新建完Sleeper和Joiner后,对grumpy调用interrupt,grumpy立即苏醒,输出:
grumpy was interrupted. interrupted(): false
grumpy has awakened
Doc join completed/(这里interrupted()之所以是false是由于异常被捕获时将清理isInterrupted()的返回值,所以在捕获语句中一旦异常被捕获,isInterrupted()总是为false/
苏醒后,doc马上就join.
然后程序继续运行,这个时候sleepy休眠了1500ms后也苏醒了,dopey也就马上join进去,所以输出了最后两段。
之后应该还会有三篇,最近立了好多flag。
话说markdown感觉比latex好玩啊