并发编程
一直以为并发只是Java的一部分,没有想到这部分这么多,所以只能以系列的来记录,因为一篇的话根本描述不完,预计一个月的时间去了解实践。像书中讲的那样,你即使不了解它,你的程序也许也会正常的执行,偶尔出个问题,你也会觉得获取是其他地方的问题,久而久之并发的问题就不了了之了,但肯定会酝酿一个更大的bug来迎接你。
也是因为新环境的问题,认识到自己的不足,即使以前我也看过很多遍的,每次看的都比较煎熬,那么痛苦的环境下就放弃了,也是因为太急功近利,没有把心沉下来,好好的去探索里面的奥秘,像书中而言,就当学习一门新的语言了。
- 顺序编程:即程序只能按照既定的顺序去执行。以前从来没有过这概念,但细想来确实,并发以前的程序
- 并发的作用:并发解决的问题主要还是”速度“和”任务可管理“
并发的作用
-
速度
并发的初衷在于阻塞:当单核处理器时,并发程序其实开销会更大一些,因为还牵扯到线程(上下文)切换的操作,将程序作为单个的任务可能会更快一点,开销更少一点,但是 但是 但是 单个任务执行的时候,如果发生堵塞,那整个应用将停止运行,直至崩溃,并发处理的话却不会发生这种问题,一个任务停止的话,还有其他的任务可以执行,如果没有堵塞,在单核处理器上并发将没有意义;多核处理器的话,不适用并发的话,那会造成多大的浪费。
-
改进代码设计
在我看来,就是对代码结构的优化,文字看的不是太透彻,不对 是因为翻译的真的太懒了。
基本的线程机制
-
任务
1.Runnable是一个任务,是需要依附在线程上去启动的。实现Runnable需要重写一个run方法,如果这个任务没有依附在线程上的话,那么也就是个普通的方法,不具备任何的线程能力。
2.Runnable的run方法是执行在新线程的,构造则是执行在原线程的(此处为main线程)。
public class LiftOff implements Runnable {
// 成员变量 每个对象都有自己在内存中的一片区域,所以每个对象都有自己的成员
protected int countDown = 10; // Default
// 静态成员 成员不属于对象 存在于内存(静态储存区)中
private static int taskCount = 0;
// 成员 final 对象只初始化一次
private final int id = taskCount++;
public LiftOff() {
}
public LiftOff(int countDown) {
this.countDown = countDown;
}
public String status() {
return "#" + id + "(" +
(countDown > 0 ? countDown : "Liftoff!") + "), ";
}
public void run() {
try {
while (countDown-- > 0) {
System.out.print(status());
Thread.yield();
TimeUnit.MILLISECONDS.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// LiftOff liftOff = new LiftOff();
// 一个普通的run方法执行
// liftOff.run();
// LiftOff的run方法是执行在新线程的
// 构造函数执行在新线程的开启线程(main线程)
// Thread thread = new Thread(new LiftOff());
// 开启一个线程 执行任务的run方法
// thread.start();
}
}
-
线程池
1.Executors.newCachedThreadPool() 这个模式,差不多就是有多少任务创建多少线程的意思
2.Executors.newFixedThreadPool(3) 这个模式 传入对应的int参数 根据参数创建对应数量的线程池
3.Executors.newSingleThreadExecutor() 创建单个线程池,如果提交多个任务,将会排队执行
public static void main(String[] args) { // ExecutorService executorService = Executors.newCachedThreadPool(); // ExecutorService executorService = Executors.newFixedThreadPool(3); ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 3; i++) { executorService.execute(new LiftOff()); } executorService.shutdown(); }
-
Callable
有返回值的任务:实现Callable接口,重写call方法,线程池通过submit提交Callable获取Future数据,根据1.future.isDone()查看是否有结果
2.future.get() 获取结果
class TaskWithResult implements Callable<String> { private int id; public TaskWithResult(int id) { this.id = id; } public String call() { return "result of TaskWithResult " + id; } } public class CallableDemo { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); try { Future<String> submit = exec.submit(new TaskWithResult(9)); String s = submit.get(); System.out.println(s); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); }finally { exec.shutdown(); } } }
-
Priority 优先级
我在AS上测试了几下,感觉实用性不大
-
Thread.yield()
让位机制,当前程序的主要工作完成以后,就可以让出CPU执行权限了,让位于其他线程,但线程里面的机制吧,你让位了也不一定丢失掉执行权限,你没让位不一定不丢失,所以不建议使用(官方建议,不依赖于此).
-
Daemon 后台线程
后台线程:初始化线程后,可以通过setDaemon()将线程设置为后台线程(在start()开启之前);线程池通过Executors.newCachedThreadPool(new DaemonThreadFactory())设置(DaemonThreadFactory跟普通的唯一区别就是将Deaemon设置为了true,设置为了后台线程);
1.后台线程是特别没有地位的线程,当非后台结束的时候,后台线程就连带着也终止了。
2.Executors.newCachedThreadPool(new ThreadFactory())通过传入的参数创建新的线程。
3.通过isDaemon判断是否为后台线程,而且在后台线程里创建的线程也是后台线程。
public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool( new DaemonThreadFactory()); for (int i = 0; i < 10; i++) exec.execute(new DaemonFromFactory()); print("All daemons started"); TimeUnit.MILLISECONDS.sleep(500); // Run for a while }
-
关于线程的一些变体
关于Thread和Runable的一些实现方式,推荐几种内部类的方式,但我觉得都还好,没有太惊艳的东西,可能是level太低,看不懂吧
class InnerThread1 { private int countDown = 5; private Inner inner; private class Inner extends Thread { Inner(String name) { super(name); start(); } public void run() { try { while (true) { print(this); if (--countDown == 0) return; sleep(10); } } catch (InterruptedException e) { print("interrupted"); } } public String toString() { return getName() + ": " + countDown; } } public InnerThread1(String name) { inner = new Inner(name); } }
-
加入一个线程
标题是加入一个线程,其实我感觉叫“抢夺执行权”比较贴切,主要是Thread.join方法
- 当A线程在B线程里调用A.join方法时,B线程会先挂起,等A线程执行完再执行。
- 当A调用A.interrupt方法时,join方法将被中断执行,此刻执行A.interrupt的执行线程也会一同结束。(里面牵扯到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) {
print(getName() + " was interrupted. " +
"isInterrupted(): " + isInterrupted());
return;
}
print(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) {
print("Interrupted");
}
print(getName() + " join completed");
}
}
public class Joining {
public static void main(String[] args) {
Sleeper sleepy = new Sleeper("Sleepy", 1500);
Sleeper grumpy = new Sleeper("Grumpy", 1500);
Joiner dopey = new Joiner("Dopey", sleepy);
Joiner doc = new Joiner("Doc", grumpy);
grumpy.interrupt();
}
}
-
捕获异常
说是捕捉线程中逃跑的异常,但我感觉大概就是运行时异常吧
可以通过一个静态方法setDefaultUncaughtExceptionHandler配置一个异常处理器,
class ExceptionThread2 implements Runnable {
public void run() {
throw new NullPointerException();
}
}
class MyUncaughtExceptionHandler implements
Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.println("caught " + e);
}
}
class HandlerThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(
new MyUncaughtExceptionHandler());
return t;
}
}
public class CaptureUncaughtException {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool(
new HandlerThreadFactory());
exec.execute(new ExceptionThread2());
}
}
也可以通过自定义异常处理器处理
public class CaptureUncaughtException {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool(
new HandlerThreadFactory());
exec.execute(new ExceptionThread2());
}
}
class HandlerThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(
new MyUncaughtExceptionHandler());
return t;
}
}
class MyUncaughtExceptionHandler implements
Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.println("caught " + e);
}
}
class ExceptionThread2 implements Runnable {
public void run() {
throw new RuntimeException();
}
}
还有最简单的一种方式,在不存在专有未捕获异常处理器时调用
public class SettingDefaultHandler {
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(
new MyUncaughtExceptionHandler());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}
}
class MyUncaughtExceptionHandler implements
Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.println("caught " + e);
}
}
public class ExceptionThread implements Runnable {
public void run() {
throw new RuntimeException();
}
}
网友评论