并发编程指南之线程管理

与每个Java语言中的元素一样,线程是对象。在Java中,我们有2个方式创建线程:
     通过直接继承thread类,然后覆盖run()方法。

    构建一个实现Runnable接口的类, 然后创建一个thread类对象并传递Runnable对象作为构造参数

1. package test;
2. public class Calculator implements Runnable {
3. private int number;
4. public Calculator(int number) {
5. super();
6. this.number = number;
7. }
8. @Override
9. public void run() {
10. // TODO Auto-generated method stub
11. for (int i = 0; i < 10; i++) {
12. System.out.printf("%s: %d * %d = %d\n",Thread.currentThread().getName(), number , i , i*number);
13. }
14. }
15. }
1. public class Main {
2. public static void main(String[] args) {
3. for (int i = 0; i < 10; i++) {
4. Thread thread = new Thread(new Calculator(i));
5. thread.start();
6. }
7. }
8. }
每个Java程序最少有一个执行线程。当你运行程序的时候, JVM运行负责调用main()方法的执行线程。
当调用Thread对象的start()方法时, 我们创建了另一个执行线程。在这些start()方法调出后,我们的程序就有了多个执行线程。
当全部的线程执行结束时(更具体点,所有非守护线程结束时),Java程序就结束了。如果初始线程(执行main()方法的主线程)运行结束,其他的线程还是会继续执行直到执行完成。但是如果某个线程调用System.exit()指示终结程序,那么全部的线程都会结束执行。
创建一个Thread类的对象不会创建新的执行线程。同样,调用实现Runnable接口的 run()方法也不会创建一个新的执行线程。只有调用start()方法才能创建一个新的执行线程。
获取和设置线程信息::::::::::::
Thread类的对象中保存了一些属性信息能够帮助我们来辨别每一个线程,知道它的状态,调整控制其优先级。 这些属性是:
ID: 每个线程的独特标识。
Name: 线程的名称。
Priority: 线程对象的优先级。优先级别在1-10之间,1是最低级,10是最高级。不建议改变它们的优先级,但是你想的话也是可以的。
Status: 线程的状态。在Java中,线程只能有这6种中的一种状态: new, runnable, blocked, waiting, time waiting, 或 terminated.
在这个指南里,我们将开发一个为10个线程设置名字和优先级的程序,然后展示它们的状态信息直到线程结束。这些线程会计算数字乘法表。
1. public class Calculator implements Runnable {
2. private int number;
3. public Calculator(int number) {
4. super();
5. this.number = number;
6. }
7. @Override
8. public void run() {
9. // TODO Auto-generated method stub
10. for (int i = 0; i < 10; i++) {
11. System.out.printf("%s: %d * %d = %d\n",Thread.currentThread().getName(), number , i , i*number);
12. }
13. }
14. }
1. public class Main {
2. public static void main(String[] args) throws Exception {
3. Thread threads[] = new Thread[10];
4. Thread.State states[] = new Thread.State[10];
5. //设置每个线程优先级及名称
6. for (int i = 0; i < 10; i++) {
7. threads[i] = new Thread(new Calculator(i));
8. if ((i%2) == 0) {
9. threads[i].setPriority(Thread.MAX_PRIORITY);
10. } else {
11. threads[i].setPriority(Thread.MIN_PRIORITY);
12. }
13. threads[i].setName("thread" + i);
14. }
15. //将线程状态的改变写入文档
16. FileWriter file = new FileWriter(".\\log.txt");
17. PrintWriter pw = new PrintWriter(file);
18. for (int i = 0; i < 10; i++) {
19. pw.println("Main : Status of thread " + i +":"+threads[i].getState());
20. states[i] = threads[i].getState();
21. }
22. pw.flush();
23. //执行线程
24. for (int i = 0; i < 10; i++) {
25. threads[i].start();
26. }
27. boolean finish = false;
28. while (!finish) {
29. for (int i = 0; i < 10; i++) {
30. if (threads[i].getState() != states[i]) {
31. writeThreadInfo(pw,threads[i],states[i]);
32. states[i] = threads[i].getState();
33. }
34. }
35. finish = true;
36. for (int i = 0; i < 10; i++) {
37. finish = finish && (states[i] == State.TERMINATED);
38. if (!finish) {
39. break;
40. }
41. }
42. }
43. }
44. private static void writeThreadInfo(PrintWriter pw, Thread thread, State state) {
45. // TODO Auto-generated method stub
46. pw.printf("Main : Id %d - %s\n",thread.getId(),thread.getName());
47. pw.printf("Main : Priority: %d\n",thread.getPriority());
48. pw.printf("Main : Old State: %s\n",state);
49. pw.printf("Main : New State: %s\n",thread.getState());
50. pw.printf("Main : ************************************\n");
51. pw.flush();
52. }
53. }
可以用Thread类的静态方法currentThread()来访问正在运行的Runnable 对象的 Thread对象
线程中断:::
一个多个线程在执行的Java程序,只有当其全部的线程执行结束时(更具体的说,是所有非守护线程结束或者某个线程调用System.exit()方法的时候),它才会结束运行。有时,你需要为了终止程序而结束一个线程,或者当程序的用户想要取消某个Thread对象正在做的任务。
java提供中断机制来通知线程表明我们想要结束它。中断机制的特性是线程需要检查是否被中断,而且还可以决定是否响应结束的请求。所以,线程可以忽略中断请求并且继续运行。
1. public class PrimeGenerator extends Thread {
2. @Override
3. public void run() {
4. // TODO Auto-generated method stub
5. long number = 1L;
6. while (true) {
7. if (isPrime(number)) {
8. System.out.printf("number %d is prime\n",number);
9. }
10. if (isInterrupted()) {
11. System.out.println("the prime generator has been interrupted");
12. return ;
13. }
14. number++;
15. }
16. }
17. private boolean isPrime(long number) {
18. // TODO Auto-generated method stub
19. if (number <= 2) {
20. return true;
21. }
22. for (long i = 2; i < number; i++) {
23. if (number % i == 0) {
24. return false;
25. }
26. }
27. return true;
28. }
29. }
1. public class Main {
2. public static void main(String[] args) {
3. Thread task = new PrimeGenerator();
4. task.start();
5. try {
6. Thread.sleep(5000);
7. } catch (Exception e) {
8. // TODO: handle exception
9. }
10. task.interrupt();
11. }
12. }

isInterrupted()和interrupted() 方法有着很重要的区别。
第一个不会改变interrupted属性值,但是第二个会设置成false。
interrupted() 方法是一个静态方法,建议使用isInterrupted()方法。
你学习了如何中断执行线程和如何对Thread对象的中断控制。之前例子中的机制(直接使用一个return返回了)可以在很容易中断的线程中使用。但是如果线程实现的是由复杂的算法分成的一些方法,或者它的方法有递归调用,那么我们可以用更好的机制来控制线程中断。为了这个Java提供了InterruptedException异常。当你检测到程序的中断并在run()方法内捕获,你可以抛这个异常。
1. package demo3;
2. import java.io.File;
3. public class FileSearch implements Runnable {
4. private String initPath;
5. private String fileName;
6. @Override
7. public void run() {
8. // TODO Auto-generated method stub
9. File file = new File(initPath);
10. if (file.isDirectory()) {
11. try {
12. directoryProcess(file);
13. } catch (InterruptedException e) {
14. System.out.println(Thread.currentThread().getName()+": thre search has been interrupted");
15. }
16. }
17. }
18. private void directoryProcess(File file) throws InterruptedException{
19. // TODO Auto-generated method stub
20. File list[] = file.listFiles();
21. if (list != null) {
22. for (int i = 0; i < list.length; i++) {
23. System.out.println(list[i].getAbsolutePath());
24. if (list[i].isDirectory()) {
25. directoryProcess(list[i]);
26. }else {
27. fileProcess(list[i]);
28. }
29. }
30. }
31. if (Thread.interrupted()) {
32. throw new InterruptedException();
33. }
34. }
35. private void fileProcess(File file) throws InterruptedException{
36. // TODO Auto-generated method stub
37. if (file.getName().equals(fileName)) {
38. System.out.println("找到该文件"+file.getName());
39. }
40. if (Thread.interrupted()) {
41. throw new InterruptedException();
42. }
43. }
44. public FileSearch(String initPath, String fileName) {
45. super();
46. this.initPath = initPath;
47. this.fileName = fileName;
48. }
49. }
1. import java.util.concurrent.TimeUnit;
2. public class Main {
3. public static void main(String[] args) {
4. FileSearch fileSearch = new FileSearch("c:\\", "che.txt");
5. Thread thread = new Thread(fileSearch);
6. thread.start();
7. try {
8. TimeUnit.SECONDS.sleep(100);
9. } catch (Exception e) {
10. // TODO: handle exception
11. e.printStackTrace();
12. }
13. thread.interrupt();
14. }
15. }

有时, 你会感兴趣在一段确定的时间内中断执行线程。例如, 程序的一个线程每分钟检查反应器状态。其余时间,线程什么也不做。在这段时间,线程不使用任何计算机资源。过了这段时间,当JVM选择它时,线程会准备好继续执行。为达此目的,你可以使用Thread类的 sleep() 方法 。此方法接收一个整数作为参数,表示线程暂停运行的毫秒数。 在调用sleep() 方法后,当时间结束时,当JVM安排他们CPU时间,线程会继续按指令执行,  
另一种可能是使用一个有TimeUnit列举元素的sleep() 方法,使用线程类的 sleep() 方法让当前线程睡眠,但是它接收的参数单位是表示并转换成毫秒的。
1. package demo4;
2. import java.util.Date;
3. import java.util.concurrent.TimeUnit;
4. public class FileClock implements Runnable{
5. @Override
6. public void run() {
7. // TODO Auto-generated method stub
8. for (int i = 0; i < 10; i++) {
9. System.out.println(new Date().toString());
10. try {
11. TimeUnit.SECONDS.sleep(1);
12. } catch (InterruptedException e) {
13. System.out.println("the fileclock has been interrupted");
14. }
15. }
16. }
17. }
1. package demo4;
2. import java.util.concurrent.TimeUnit;
3. public class Main {
4. public static void main(String[] args) {
5. FileClock fileClock = new FileClock();
6. Thread thread = new Thread(fileClock);
7. thread.start();
8. try {
9. TimeUnit.SECONDS.sleep(10);
10. } catch (Exception e) {
11. // TODO: handle exception
12. }
13. thread.interrupt();
14. }
15. }
你可以发现程序是如何每秒写入一个Date对象的,然后,有信息显示FileClock线程被中断。
当你调用sleep()方法, Thread 离开CPU并在一段时间内停止运行。在这段时间内,它是不消耗CPU时间的,使其可以执行其他任务。
当 Thread is是睡眠和中断的时候,那方法会立刻抛出InterruptedException异常并不会一直等到睡眠时间过去
Java 并发 API 有另一种方法能让线程对象离开 CPU。它是 yield() 方法, 它向JVM表示线程对象可以让CPU执行其他任务。JVM 不保证它会遵守请求。通常,它只是用来试调的。

等待线种的终结::::::
在某些情况下,我们需要等待线程的终结。例如,我们可能会遇到程序在执行前需要初始化资源。在执行剩下的代码之前,我们需要等待线程完成初始化任务。
为达此目的, 我们使用Thread 类的join() 方法。当前线程调用某个线程的这个方法时,它会暂停当前线程,直到被调用线程执行完成。
1. import java.util.Date;
2. import java.util.concurrent.TimeUnit;
3. public class DataSourceLoader implements Runnable{
4. @Override
5. public void run() {
6. // TODO Auto-generated method stub
7. System.out.println("beginnning data source loading:"+new Date());
8. try {
9. TimeUnit.SECONDS.sleep(4);
10. } catch (Exception e) {
11. // TODO: handle exception
12. }
13. System.out.println("finishing data source loading:"+new Date());
14. }
15. }
1. public class NetworkSourceLoader implements Runnable{
2. @Override
3. public void run() {
4. // TODO Auto-generated method stub
5. System.out.println("beginnning data source loading:"+new Date());
6. try {
7. TimeUnit.SECONDS.sleep(6);
8. } catch (Exception e) {
9. // TODO: handle exception
10. }
11. System.out.println("finishing data source loading:"+new Date());
12. }
13. }
1. public class Main {
2. public static void main(String[] args) {
3. Thread thread = new Thread(new DataSourceLoader());
4. thread.start();
5. Thread thread2 = new Thread(new NetworkSourceLoader());
6. thread2.start();
7. try {
8. thread.join();
9. thread2.join();
10. } catch (Exception e) {
11. // TODO: handle exception
12. }
13. System.out.println("main: configuration has been loaded");
14. }
15. }
Java 提供2种形式的 join() 方法:
join (long milliseconds)
join (long milliseconds, long nanos)
第一种join() 方法, 这方法让调用线程等待特定的毫秒数。例如,如果thread1对象使用代码thread2.join(1000), 那么线程 thread1暂停运行,直到以下其中一个条件发生:
thread2 结束运行
1000 毫秒过去了
当其中一个条件为真时,join() 方法返回。
第二个版本的 join() 方法和第一个很像,只不过它接收一个毫秒数和一个纳秒数作为参数。

守护线程的创建和运行:::::::::::::::
Java有一种特别的线程叫做守护线程。这种线程的优先级非常低,通常在程序里没有其他线程运行时才会执行它。当守护线程是程序里唯一在运行的线程时,JVM会结束守护线程并终止程序。
根据这些特点,守护线程通常用于在同一程序里给普通线程(也叫使用者线程)提供服务。它们通常无限循环的等待服务请求或执行线程任务。它们不能做重要的任务,因为我们不知道什么时候会被分配到CPU时间片,并且只要没有其他线程在运行,它们可能随时被终止。JAVA中最典型的这种类型代表就是垃圾回收器。
1. import java.util.Date;
2. public class Event {
3. private Date date;
4. private String event;
5. public Date getDate() {
6. return date;
7. }
8. public void setDate(Date date) {
9. this.date = date;
10. }
11. public String getEvent() {
12. return event;
13. }
14. public void setEvent(String event) {
15. this.event = event;
16. }
17. }
1. public class WriterTask implements Runnable{
2. private Deque<Event> deque;
3. public WriterTask(Deque<Event> deque) {
4. super();
5. this.deque = deque;
6. }
7. @Override
8. public void run() {
9. // TODO Auto-generated method stub
10. for (int i = 0; i < 100; i++) {
11. Event event = new Event();
12. event.setDate(new Date());
13. event.setEvent(String.format("the thread %s has generated an event", Thread.currentThread().getId()));
14. deque.addFirst(event);
15. try {
16. TimeUnit.SECONDS.sleep(1);
17. } catch (Exception e) {
18. // TODO: handle exception
19. }
20. }
21. }
22. }
1. public class CleanerTask extends Thread{
2. private Deque<Event> deque;
3. public CleanerTask(Deque<Event> deque) {
4. super();
5. this.deque = deque;
6. setDaemon(true);//设置为守护线程
7. }
8. @Override
9. public void run() {
10. // TODO Auto-generated method stub
11. while(true){
12. Date date = new Date();
13. clean(date);
14. }
15. }
16. private void clean(Date date) {
17. // TODO Auto-generated method stub
18. if (deque.size() == 0) {
19. return;
20. }
21. long difference = 0;
22. boolean delete = false;
23. do {
24. Event e = deque.getLast();
25. difference = date.getTime() - e.getDate().getTime();
26. if (difference > 10000) {
27. System.out.println("cleaner: "+e.getEvent());
28. deque.removeLast();
29. delete = true;
30. }
31. } while (difference > 10000);
32. if (delete) {
33. System.out.println("cleaner: size of the deque::"+deque.size());
34. }
35. }
36. }
1. public class Main {
2. public static void main(String[] args) {
3. Deque<Event> deque = new ArrayDeque<>();
4. WriterTask wt = new WriterTask(deque);
5. for (int i = 0; i < 3; i++) {
6. Thread thread = new Thread(wt);
7. thread.start();
8. }
9. CleanerTask ct = new CleanerTask(deque);
10. ct.start();
11. }
12. }
你可以发现queue可以一直增加直到它有30个事件,然后它的大小会在27-30之间直到运行结束。  
程序开始时有3个 WriterTask 线程。每个线程写一个事件然后休眠1秒。10秒之后,我们有30个事件在queue里。在这10秒内,当3个 WriterTask 线程休眠时, CleanerTasks已经开始运行,但是它没有删除任何事件,因为所有事件都才生成不到10秒。在剩下的运行里,CleanerTask 每秒删除3个事件, 然而3个 WriterTask 线程会另写3个,所以queue的大小在27-30之间。
你可以修改 WriterTask 线程的休眠时间。如果你使用一个较小的值,你会发现CleanerTask 被分配到 CPU 时间片会更少,由于 CleanerTask 没有删除任何事件,所以queue大小会一直增加。  
只能在start() 方法之前可以调用 setDaemon() 方法。一旦线程运行了,就不能修改守护状态。  
可以使用 isDaemon() 方法来检查线程是否是守护线程(方法返回 true) 或者是使用者线程 (方法返回 false)。

在线程里处理不受控制的异常::::::::::::::::
Java里有2种异常:
检查异常(Checked exceptions): 这些异常必须强制捕获它们或在一个方法里的throws子句中。 例如, IOException 或者ClassNotFoundException。
未检查异常(Unchecked exceptions): 这些异常不用强制捕获它们。例如, NumberFormatException。
在一个线程 对象的 run() 方法里抛出一个检查异常,我们必须捕获并处理他们。因为 run() 方法不接受 throws 子句。当一个非检查异常被抛出,默认的行为是在控制台写下stack trace并退出程序。
幸运的是, Java 提供我们一种机制可以捕获和处理线程对象抛出的未检测异常来避免程序终结。
1. public class ExceptionHandler implements UncaughtExceptionHandler {
2. @Override
3. public void uncaughtException(Thread t, Throwable e) {
4. // TODO Auto-generated method stub
5. System.out.println("an exception has been captured");
6. System.out.println("thread "+ t.getId());
7. System.out.println("exception :"+e.getClass().getName()+":"+e.getMessage());
8. e.printStackTrace(System.out);
9. System.out.println("thread status "+ t.getState());
10. }
11. }
1. public class Task implements Runnable{
2. @Override
3. public void run() {
4. // TODO Auto-generated method stub
5. int number = Integer.parseInt("aaa");//未检查异常
6. }
7. }
1. public class Main {
2. public static void main(String[] args) {
3. Thread thread = new Thread(new Task());
4. thread.setUncaughtExceptionHandler(new ExceptionHandler());
5. thread.start();
6. }
7. }
当在一个线程里抛出一个异常,但是这个异常没有被捕获(这肯定是非检查异常), JVM 检查线程的相关方法是否有设置一个未捕捉异常的处理者 。如果有,JVM 使用Thread 对象和 Exception 作为参数调用此方法 。  
如果线程没有捕捉未捕获异常的处理者, 那么 JVM会把异常的 stack trace 写入操控台并结束任务。
The Thread 类有其他相关方法可以处理未捕获的异常。静态方法 setDefaultUncaughtExceptionHandler() 为应用里的所有线程对象建立异常 handler 。
当一个未捕捉的异常在线程里被抛出,JVM会寻找此异常的3种可能潜在的处理者(handler)。  
首先, 它寻找这个未捕捉的线程对象的异常handle,如我们在在这个指南中学习的。如果这个handle 不存在,那么JVM会在线程对象的ThreadGroup里寻找非捕捉异常的handler,如在处理线程组内的不受控制异常里介绍的那样。如果此方法不存在,正如我们在这个指南中学习的,那么 JVM 会寻找默认非捕捉异常handle。  
如果没有一个handler存在, 那么 JVM会把异常的 stack trace 写入操控台并结束任务。

使用本地线程变量:::::::::::::::::
并发应用的一个关键地方就是共享数据。这个对那些扩展Thread类或者实现Runnable接口的对象特别重要。
如果你创建一个类对象,实现Runnable接口,然后多个Thread对象使用同样的Runnable对象,全部的线程都共享同样的属性。这意味着,如果你在一个线程里改变一个属性,全部的线程都会受到这个改变的影响。
有时,你希望程序里的各个线程的属性不会被共享。 Java 并发 API提供了一个很清楚的机制叫本地线程变量。
1. public class UnsafeTask implements Runnable{
2. private Date startDate;
3. @Override
4. public void run() {
5. // TODO Auto-generated method stub
6. startDate = new Date();
7. System.out.println("starting thread:"+Thread.currentThread().getId()+":"+startDate);
8. try {
9. TimeUnit.SECONDS.sleep((long) Math.rint(Math.random()*10));
10. } catch (Exception e) {
11. }
12. System.out.println("stoping thread:"+Thread.currentThread().getId()+":"+startDate);
13. }
14. }
1. public class Main {
2. public static void main(String[] args) {
3. UnsafeTask unsafeTask = new UnsafeTask();
4. for (int i = 0; i < 10; i++) {
5. Thread thread = new Thread(unsafeTask);
6. thread.start();//所有的这些线程都有着相同的结束时间。没有使用本地线程变量以前
7. }
8. try {
9. TimeUnit.SECONDS.sleep(2);
10. } catch (Exception e) {
11. // TODO: handle exception
12. } 
13. }
14. }
使用本地线程变量以后:::::::
1. import java.util.Date;
2. import java.util.concurrent.TimeUnit;
3. public class UnsafeTask implements Runnable{
4. //本地线程变量机制
5. private static ThreadLocal<Date> startDate = new ThreadLocal<Date>(){
6. protected Date initialValue() {
7. return new Date();
8. };
9. };
10. @Override
11. public void run() {
12. System.out.println("starting thread:"+Thread.currentThread().getId()+":"+startDate.get());
13. try {
14. TimeUnit.SECONDS.sleep((long) Math.rint(Math.random()*10));
15. } catch (Exception e) {
16. }
17. System.out.println("stoping thread:"+Thread.currentThread().getId()+":"+startDate.get());
18. }
19. }

本地线程变量为每个使用这些变量的线程储存属性值。可以用 get() 方法读取值和使用 set() 方法改变值。 如果第一次你访问本地线程变量的值,如果没有值给当前的线程对象,那么本地线程变量会调用 initialValue() 方法来设置值给线程并返回初始值。
本地线程类还提供 remove() 方法,删除存储在线程本地变量里的值。
Java 并发 API 包括 InheritableThreadLocal 类提供线程创建线程的值的遗传性 。如果线程A有一个本地线程变量,然后它创建了另一个线程B,那么线程B将有与A相同的本地线程变量值。 你可以覆盖 childValue() 方法来初始子线程的本地线程变量的值。 它接收父线程的本地线程变量作为参数。
线程组::::::::::::::::
java并发 API里有个有趣的方法是把线程分组。这个方法允许我们按线程组作为一个单位来处理。例如,你有一些线程做着同样的任务,你想控制他们,无论多少线程还在运行,他们的状态会被一个call 中断。
Java 提供 ThreadGroup 类来组织线程。 ThreadGroup 对象可以由 Thread 对象组成和由另外的 ThreadGroup 对象组成,生成线程树结构。
1. public class Result {
2. private String name;
3. public String getName() {
4. return name;
5. }
6. public void setName(String name) {
7. this.name = name;
8. }
9. }
1. public class SearchTask implements Runnable{
2. private Result result;
3. public SearchTask(Result result) {
4. super();
5. this.result = result;
6. }
7. @Override
8. public void run() {
9. String name = Thread.currentThread().getName();
10. System.out.println("thread: start::"+name);
11. try {
12. doTask();
13. result.setName(name);
14. } catch (InterruptedException e) {
15. System.out.println("thread is interrupted:"+name);
16. return;
17. }
18. System.out.println("thread end ::"+name);
19. }
20. private void doTask() throws InterruptedException {
21. Random random = new Random((new Date()).getTime());
22. int value = (int)(random.nextDouble()*100);
23. System.out.println("thread : "+Thread.currentThread().getName());
24. TimeUnit.SECONDS.sleep(value);
25. }
26. }
1. public class Main {
2. public static void main(String[] args) {
3. ThreadGroup threadGroup = new ThreadGroup("searcher");
4. Result result = new Result();
5. SearchTask searchTask = new SearchTask(result);
6. for (int i = 0; i < 5; i++) {
7. Thread thread = new Thread(threadGroup, searchTask);
8. thread.start();
9. }
10. try {
11. TimeUnit.SECONDS.sleep(1);
12. } catch (Exception e) {
13. }
14. System.out.println("number of thread::"+threadGroup.activeCount());
15. System.out.println("information about the threadgroup");
16. threadGroup.list();
17. Thread[] threads = new Thread[threadGroup.activeCount()];//activeCount获取线程个数
18. //与ThreadGroup对象关联的线程的列表
19. threadGroup.enumerate(threads);
20. for (int i = 0; i < threads.length; i++) {
21. System.out.println("thread "+ threads[i].getName()+":thread status"+threads[i].getState());
22. }
23. waitFinish(threadGroup);//等待其中一个线程结束
24. threadGroup.interrupt();//阻断其它线程
25. }
26. private static void waitFinish(ThreadGroup threadGroup) {
27. // TODO Auto-generated method stub
28. while (threadGroup.activeCount()>9) {
29. try {
30. TimeUnit.SECONDS.sleep(1);
31. } catch (Exception e) {
32. // TODO: handle exception
33. }
34. }
35. }
36. }
处理线程组内不受控制异常:::::::
1. public class MyThreadGroup extends ThreadGroup {
2. public MyThreadGroup(String name) {
3. super(name);
4. }
5. @Override
6. public void uncaughtException(Thread t, Throwable e) {
7. System.out.println("thre thread "+t.getId()+"has thrown an exception");
8. e.printStackTrace(System.out);
9. System.out.println("terminating the of thre thread");
10. interrupt();
11. }
12. }

你会发现线程对象是如何抛出异常和其他被中断的。当一个非捕捉异常在线程内抛出,JVM会为这个异常寻找3种可能handlers。
首先, 它寻找这个未捕捉的线程对象的异常handle,如在线程中处理不受控制异常中介绍的,如果这个handle 不存在,那么JVM会在线程对象的ThreadGroup里寻找非捕捉异常的handler,如我们在这个指南中学习的。如果此方法不存在,那么 JVM 会寻找默认非捕捉异常handle。如果没有 handlers存在, 那么 JVM会把异常的 stack trace 写入控制台并结束任务。

用线程工厂创建线程:::::::::::::::::::::::::
在面向对象编程的世界中,工厂模式是最有用的设计模式。它是一个创造模式,还有它的目的是创建一个或几个类的对象的对象。然后,当我们想创建这些类的对象时,我们使用工厂来代替new操作。
有了这个工厂,我们有这些优势来集中创建对象们:
更简单的改变了类的对象创建或者说创建这些对象的方式。
更简单的为了限制的资源限制了对象的创建。 例如, 我们只new一个此类型的对象。
更简单的生成创建对象的统计数据。
Java提供一个接口, ThreadFactory 接口实现一个线程对象工厂。 并发 API 使用线程工厂来创建线程的一
1. package demo10;
2. import java.util.ArrayList;
3. import java.util.Date;
4. import java.util.Iterator;
5. import java.util.List;
6. import java.util.concurrent.ThreadFactory;
7. public class MyThreadFactory implements ThreadFactory{
8. private int counter;
9. private String name;
10. private List<String> stats;
11. public MyThreadFactory (String name) {
12. counter = 0;
13. this.name = name;
14. stats = new ArrayList<>();
15. }
16. @Override
17. public Thread newThread(Runnable r) {
18. Thread t = new Thread(r, "thread"+counter);
19. counter ++;
20. stats.add(String.format("create thread %d with name %s on %s\n", t.getId(),t.getName(),new Date()));
21. return t;
22. }
23. public String getStats(){
24. StringBuffer sbf = new StringBuffer();
25. Iterator<String> it = stats.iterator();
26. while (it.hasNext()) {
27. String string = (String) it.next();
28. sbf.append(string);
29. sbf.append("\n");
30. }
31. return sbf.toString();
32. }
33. }
1. package demo10;
2. import java.util.concurrent.TimeUnit;
3. public class Task implements Runnable {
4. @Override
5. public void run() { 
6. try {
7. TimeUnit.SECONDS.sleep(1);
8. } catch (Exception e) {
9. // TODO: handle exception
10. }
11. }
12. }
1. package demo10;
2. public class Main {
3. public static void main(String[] args) {
4. MyThreadFactory myThreadFactory = new MyThreadFactory("mythreadfactory");
5. Task task = new Task();
6. for (int i = 0; i < 10; i++) {
7. Thread thread = myThreadFactory.newThread(task);
8. thread.start();
9. System.out.println(myThreadFactory.getStats());
10. }
11. }
12. }

ThreadFactory 接口只有一个方法是 newThread。它接收 Runnable 对象作为参数并返回一个 Thread 对象。当你实现一个 ThreadFactory 接口,你必须实现此接口并覆盖这个方法。最基本的 ThreadFactory只有一行。
return new Thread(r);
你可以加一些变量来提升这个实现:
在这个例子中创建自定义线程,名字使用特别形式或者继承Java Thread类来创建自己的Thread类。
保存线程创建数据,如之前的例子。
限制线程创建个数。
验证线程的创建。
和你能想到的任何东西。
使用工厂设计模式是很好的练习,但是,如果你实现一个 ThreadFactory 接口来集中创建线程,那么你必须再检查代码确保使用的线程都是用这个工厂创建的。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值