1.线程的创建和运行
- 继承Thread类,并且覆盖run()方法
- 创建一个实现Runnable接口的类。使用带参数的Thread构造器来创建Thread对象。这个参数就是实现Runnable接口的类的一个对象
2.线程信息的获取和设置
- ID:保存了线程的唯一标识符
- Name:保存了线程的名称
- Priority:保存了线程对象的优先级。线程的优先级是从1到10,其中1是最低优先级
- Status:保存了线程的状态。在Java中,线程的状态有6种:new、runnable、blocked、waiting、time waiting或者terminated
3.线程的中断
Thread类有一个表明线程被中断与否的属性,他存放的是布尔值。线程的interrupt()方法被调用时,这个属性就会被设置为true。isInterrupted()方法只是返回这个属性的值。
4.线程中断的控制
如果线程实现了复杂的算法并且分布在几个方法中,或者线程里有递归调用的方法,我们就得使用一个更好的机制来控制线程的中断。为了达到这个目的,Java提供了InterruptedException异常。当检查到线程中断的时候,就抛出这个异常,然后在run()中捕获并处理这个异常。
import java.io.File;
public class FileSearch implements Runnable{
private String initPath;
private String fileName;
public FileSearch(String initPath, String fileName) {
this.initPath = initPath;
this.fileName = fileName;
}
@Override
public void run() {
File file = new File(initPath);
if (file.isDirectory()) {
try {
directoryProcess(file);
} catch (InterruptedException e) {
System.out.printf("%s: The search has been interrupted",Thread.currentThread().getName());
}
}
}
private void directoryProcess(File file)throws InterruptedException {
File[] files = file.listFiles();
if (files!=null) {
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
directoryProcess(files[i]);
}else {
fileProcess(files[i]);
}
}
}
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
private void fileProcess(File file)throws InterruptedException {
if (file.getName().equals(fileName)) {
System.out.printf("%s : %s\n",Thread.currentThread().getName(),file.getAbsolutePath());
}
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
}
不管递归调用了多少次,只要线程检测到它已经被中断了,就会立即抛出Interrupted异常,然后继续执行run()方法。
5.线程的休眠和恢复
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class FileClock implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.printf("%s\n",new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.printf("The FileClock has been interrupted");
}
}
}
}
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class FileClock implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.printf("%s\n",new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.printf("The FileClock has been interrupted");
}
}
}
}
当调用sleep()方法之后,线程会释放CPU并且不再继续执行任务。在这段时间内,线程不占用CPU时钟,所以CPU可以执行其他的任务。
如果休眠中线程被中断,该方法就会立即抛出InterruptedException异常,而不需要等待到线程休眠时间结束。
- Java并发API还提供了另一个方法来使线程对象释放CPU,即yield()方法,它将通知JVM这个线程对象可以释放CPU了。JVM并不保证遵循这个要求。通常来说,yield()方法只做调试使用。
6.等待线程的终止
Thread thread1 = new Thread(new DataSourcesLoader());
Thread thread2 = new Thread(new NetworkConnectionsLoader());
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.printf("Main : Configuration has been loaded:%s\n", new Date());
运行这个程序时,你会看到两个线程对象是如何运行的。当两个子线程运行结束的时候,主线程对象才会继续运行并打印出最终的信息。
7.守护线程的创建和运行
Java里有一种特殊的线程叫做守护线程。这种线程的优先级很低,通常来说,当同一个应用程序里没有其他的线程运行的时候,守护线程才运行。当守护线程是程序中唯一运行的线程时,守护线程执行结束后,JVM也就结束了这个程序。因为这种特性,守护线程通常被用来作为同一程序中普通线程的服务提供者。他们通常是无限循环的,以等待服务请求或者执行线程的任务。
setDaemon()方法只能在start()方法被调用之前设置。一旦线程开始运行,将不能再修改守护状态。
isDaemon()方法被用来检查一个线程是不是守护线程,返回值true表示这个线程是守护线程,false表示这个线程是用户线程。
8.线程中不可控异常的处理
- 非运行时异常(Checked Exception):这种异常必须在方法声明的throws语句指定,或者在方法体内捕获。
- 运行时异常(Unchecked Exception):这种异常不必再方法声明中指定,也不需要在方法体中捕获。
import java.lang.Thread.UncaughtExceptionHandler;
public class ExceptionHandler implements UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("An exception has been captured");
System.out.println("Thread : "+t.getId());
System.out.println("Exception: "+e.getClass().getName()+": "+e.getMessage());
System.out.println("Stack Trace: ");
e.printStackTrace(System.out);
System.out.println("Thread status: "+t.getState());
}
}
public class Task implements Runnable{
@Override
public void run() {
int numero = Integer.parseInt("TTT");
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Task());
thread.setUncaughtExceptionHandler(new ExceptionHandler());;
thread.start();
}
}
当一个线程抛出了异常并且没有被捕获时(这种情况只可能是运行时异常),JVM检查这个线程是否被预置了未捕获异常处理器。如果找到,JVM将调用线程对象的这个方法,并将线程对象和异常作为传入参数。如果线程没有被预置未捕获异常处理器,JVM将打印堆栈记录到控制台,并退出程序。
- 首先,他查找线程对象的未捕获异常处理器。
- 如果找不到,JVM继续查找线程对象所在的线程组的未捕获异常处理器。
- 如果还是找不到,JVM将继续查找默认的未捕获异常处理器。
- 如果没有一个处理器存在,JVM则将堆栈异常记录打印到控制台,并退出程序。
9.线程局部变量的使用
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class SafeTask implements Runnable {
private static ThreadLocal<Date> startDate = new ThreadLocal<Date>() {
protected Date initialValue(){
return new Date();
}
};
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Starting Thread: "+Thread.currentThread().getId()+" : "+startDate.get());
try {
TimeUnit.SECONDS.sleep((int)(Math.rint(Math.random()*10)));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Thread Finished: "+Thread.currentThread().getId()+" : "+startDate.get());
}
}
线程局部变量分别为每个线程存储了各自的属性值,并提供给每个线程使用。你可以使用get()方法读取这个值,并用set()方法设置这个值。如果线程是第一次访问线程局部变量,线程局部变量可能还没有为他存储值,这个时候initialValue()方法就会被调用,并且返回当前的时间值。
10.线程的分组
public class Result {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class SearchTask implements Runnable{
private Result result;
public SearchTask(Result result) {
this.result = result;
}
@Override
public void run() {
// TODO Auto-generated method stub
String name = Thread.currentThread().getName();
System.out.println("Thread "+name+" : Start");
try {
doTask();
result.setName(name);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("Thread "+name+": Interrupted");
return;
}
System.out.println("Thread "+name+" : End");
}
private void doTask()throws InterruptedException {
Random random = new Random(new Date().getTime());
int value = (int)(random.nextDouble()*100);
System.out.println("Thread "+Thread.currentThread().getName()+" : "+value);
TimeUnit.SECONDS.sleep(value);
}
}
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("Searcher");
Result result = new Result();
SearchTask searchTask = new SearchTask(result);
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(threadGroup,searchTask);
thread.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Number of Threads "+threadGroup.activeCount());
System.out.println("Infomation about the Thread Group");
threadGroup.list();
Thread[] threads = new Thread[threadGroup.activeCount()];
threadGroup.enumerate(threads);
for (int i = 0; i < threadGroup.activeCount(); i++) {
System.out.println("Thread "+threads[i].getName()+" : "+threads[i].getState());
}
waitFinish(threadGroup);
threadGroup.interrupt();
}
private static void waitFinish(ThreadGroup threadGroup) {
while(threadGroup.activeCount()>19) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
11.线程组中不可控异常的处理
public class MyThreadGroup extends ThreadGroup{
public MyThreadGroup(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("The thread "+t.getId()+" has thrown an Exception");
e.printStackTrace(System.out);
System.out.println("Terminating the rest of the Threads");
interrupt();
}
}
public class Task implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
int result;
Random random = new Random(Thread.currentThread().getId());
while(true) {
result = 1000/((int)(random.nextDouble()*1000));
System.out.println(Thread.currentThread().getId()+" : "+result);
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getId()+" : Interrupted");
return;
}
}
}
}
public class Main {
public static void main(String[] args) {
MyThreadGroup threadGroup = new MyThreadGroup("MyThreadGroup");
Task task = new Task();
for (int i = 0; i < 2; i++) {
Thread thread = new Thread(threadGroup,task);
thread.start();
}
}
}
public class MyThreadGroup extends ThreadGroup{
public MyThreadGroup(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("The thread "+t.getId()+" has thrown an Exception");
e.printStackTrace(System.out);
System.out.println("Terminating the rest of the Threads");
interrupt();
}
}
public class Task implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
int result;
Random random = new Random(Thread.currentThread().getId());
while(true) {
result = 1000/((int)(random.nextDouble()*1000));
System.out.println(Thread.currentThread().getId()+" : "+result);
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getId()+" : Interrupted");
return;
}
}
}
}
public class Main {
public static void main(String[] args) {
MyThreadGroup threadGroup = new MyThreadGroup("MyThreadGroup");
Task task = new Task();
for (int i = 0; i < 2; i++) {
Thread thread = new Thread(threadGroup,task);
thread.start();
}
}
}
12.使用工厂类创建线程
- 更容易修改类,或者改变创建对象的方式;
- 更容易为有限资源限制创建对象的数目。例如:我们可以限制一个类型的对象不多于N个
- 更容易为创建的对象生成统计数据
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ThreadFactory;
public class MyThreadFactory implements ThreadFactory {
private int counter;
private String name;
private List<String> stats;
public MyThreadFactory(String name) {
this.counter = 0;
this.name = name;
this.stats = new ArrayList<String>();
}
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r,name+"-Thread_"+counter);
counter++;
stats.add(String.format("Created thread %d with name %s on %s\n", thread.getId(),thread.getName(),new Date()));
return thread;
}
public String getStats() {
StringBuffer buffer = new StringBuffer();
Iterator<String> iterator = stats.iterator();
while(iterator.hasNext()) {
buffer.append(iterator.next());
buffer.append("\n");
}
return buffer.toString();
}
}
import java.util.concurrent.TimeUnit;
public class Task implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
MyThreadFactory factory = new MyThreadFactory("MyThreadFactory");
Task task = new Task();
Thread thread;
System.out.println("Starting the Threads");
for (int i = 0; i < 10; i++) {
thread = factory.newThread(task);
thread.start();
}
System.out.println("Factory stats:");
System.out.println(factory.getStats());
}
}
ThreadFactory接口只有一个方法,即newThread,他以Runnable接口对象作为传入参数并且返回一个线程对象。当实现ThreadFactory接口时,必须实现覆盖这个方法。大多数基本的线程工厂类只有一行,即: return new Thread(r); 可以增加一些变化来强化实现方法覆盖。
- 创建一个个性化线程,如这个范例中使用一个特殊的格式作为线程名,或者通过继承Thread类来创建自己的线程类。
- 保存新创建的线程的统计数据,如本例。
- 限制创建的线程的数量
- 对生成的线程进行验证