一、线程的创建和运行
1. 实现Runnable接口
public class Calculator implements Runnable {
private int number;
public Calculator(int number) {
this.number = number;
}
public void run(){
for(int i=1;i<=10;i++){
System.out.printf("%s: %d * %d = %d\n",Thread.currentThread().getName(),number,i,i*number);
}
}
public static void main(String[] args) {
for(int i=1;i<=10;i++){
Thread thread = new Thread(new Calculator(i));
thread.start(); // start()启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用
// thread.run(); // run()和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!
}
}
}
2. 继承Thread类
public class Calculator extends Thread {
private int number;
public Calculator(int number) {
this.number = number;
}
public void run(){
for(int i=1;i<=10;i++){
System.out.printf("%s: %d * %d = %d\n",Thread.currentThread().getName(),number,i,i*number);
}
}
public static void main(String[] args) {
for(int i=1;i<=10;i++){
Thread calculator = new Calculator(i);
calculator.setName("Thread");
calculator.start();
}
}
}
每个Java程序最少有一个执行线程。当你运行程序的时候, JVM运行负责调用main()方法的执行线程。
当调用Thread对象的start()方法时, 我们创建了另一个执行线程。在这些start()方法调出后,我们的程序就有了多个执行线程。
当全部的线程执行结束时(更具体点,所有非守护线程结束时),Java程序就结束了。如果初始线程(执行main()方法的主线程)运行结束,其他的线程还是会继续执行直到执行完成。但是如果某个线程调用System.exit()指示终结程序,那么全部的线程都会结束执行。
创建一个Thread类的对象不会创建新的执行线程。同样,调用实现Runnable接口的 run()方法也不会创建一个新的执行线程。只有调用start()方法才能创建一个新的执行线程。
二、线程信息的获取和设置
- ID:每个线程的独特标识
- Name:线程的名称
- Priority:线程对象的优先级。优先级别在1-10之间,1是最低级,10是最高级。不建议改变它们的优先级,但是你想的话也是可以的
- Status:线程的状态。(new, runnable, blocked, waiting, time waiting, terminated)
如果你没有声明一个线程的名字,那么JVM会自动命名它为:Thread-XX,XX是一个数字。线程的ID或者状态是不可修改的。Thread类没有实现setId()和setStatus()方法来允许修改它们。
public class Calculator implements Runnable {
private int number;
public Calculator(int number) {
this.number = number;
}
public void run(){
for(int i=1;i<=10;i++){
System.out.printf("%s: %d * %d = %d\n",Thread.currentThread().getName(),number,i,i*number);
}
}
public static void main(String[] args) {
Thread threads[] = new Thread[10];
Thread.State status[] = new Thread.State[10];
for(int i=0;i<10;i++){
threads[i] = new Thread(new Calculator(i));
// setPriority()方法会抛出 IllegalArgumentException 异常,如果你设置的优先级不是在1-10之间。
if ((i%2)==0) threads[i].setPriority(Thread.MAX_PRIORITY);
else threads[i].setPriority(Thread.MIN_PRIORITY);
threads[i].setName("Thread "+i);
}
for (int i=0;i<10;i++){
threads[i].start();
}
}
}
三、线程的中断
一个多个线程在执行的Java程序,只有当其全部的线程执行结束时(更具体的说,是所有非守护线程结束或者某个线程调用System.exit()方法的时候),它才会结束运行。有时,你需要为了终止程序而结束一个线程,或者当程序的用户想要取消某个Thread对象正在做的任务。
Java提供中断机制来通知线程表明我们想要结束它。中断机制的特性是线程需要检查是否被中断,而且还可以决定是否响应结束的请求。所以,线程可以忽略中断请求并且继续运行。
在这个指南中, 我们将开发一个程序,它创建线程,然后在5秒之后,它会使用中断机制来强制结束线程。
public class PrimeGenerator extends Thread {
public void run() {
long number=1L;
while(true){
if(isPrime(number)) System.out.printf("Number %d is Prime",number);
// 处理完一个数字, 调用isInterrupted()方法来检查线程是否被中断了。如果它返回值为真,就写一个信息并结束线程的运行
if(isInterrupted()){
System.out.println("The Prime Generator has been Interrupted");
return;
}
number++;
}
}
private boolean isPrime(long number){
if(number<=2) return false;
for(long i=2;i<number;i++){
if((number%i)==0){
return false;
}
}
return true;
}
public static void main(String[] args) {
Thread task = new PrimeGenerator();
task.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 等待5秒后终中断线程
task.interrupt();
}
}
四、线程中断的控制
http://ifeve.com/thread-management-5/
五、线程的休眠和恢复
有时你会感兴趣在一段确定的时间内中断执行线程。例如, 程序的一个线程每分钟检查反应器状态。其余时间,线程什么也不做。在这段时间,线程不使用任何计算机资源。过了这段时间,当JVM选择它时,线程会准备好继续执行。为达此目的,你可以使用Thread类的sleep()方法 。此方法接收一个整数作为参数,表示线程暂停运行的毫秒数。在调用sleep()方法后,当时间结束时,当JVM安排他们CPU时间,线程会继续按指令执行,
另一种可能是使用一个有TimeUnit列举元素的sleep()方法,使用线程类的sleep()方法让当前线程睡眠,但是它接收的参数单位是表示并转换成毫秒的。
http://ifeve.com/thread-management-6/
六、等待线程的终止
Thread类的join()方法:当一个线程对象的join()方法被调用时,调用它的线程将被挂起,直到这个线程对象完成它的任务。
public class JoinTest{
public static void main(String[] args) {
Thread thread1 = new Thread(new Task1(),"Task1");
Thread thread2 = new Thread(new Task2(),"Task2");
thread1.start();
thread2.start();
try {
thread1.join(); // 若没有这两个join方法,则程序先打印"Main: OK~"
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main: OK~");
}
}
class Task1 implements Runnable{
public void run() {
System.out.println("开始执行1...");
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行完成1...");
}
}
class Task2 implements Runnable{
public void run() {
System.out.println("开始执行2...");
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行完成2...");
}
}
七、守护线程的创建和运行
Java有一种特别的线程叫做守护线程。这种线程的优先级非常低,通常在程序里没有其他线程运行时才会执行它。当守护线程是程序里唯一在运行的线程时,JVM会结束守护线程并终止程序。
根据这些特点,守护线程通常用于在同一程序里给普通线程(也叫使用者线程)提供服务。它们通常无限循环的等待服务请求或执行线程任务。它们不能做重要的任务,因为我们不知道什么时候会被分配到CPU时间片,并且只要没有其他线程在运行,它们可能随时被终止。JAVA中最典型的这种类型代表就是垃圾回收器。
http://ifeve.com/thread-management-8/
八、线程中不可控异常的处理
Java里有2种异常:
- 检查异常(Checked exceptions): 这些异常必须强制捕获它们或在一个方法里的throws子句中。 例如IOException或者ClassNotFoundException。
- 未检查异常(Unchecked exceptions): 这些异常不用强制捕获它们。例如NumberFormatException。
在一个线程对象的run()方法里抛出一个检查异常,我们必须捕获并处理他们。因为run()方法不接受throws子句。当一个非检查异常被抛出,默认的行为是在控制台写下stacktrace并退出程序。
幸运的是, Java 提供我们一种机制可以捕获和处理线程对象抛出的未检测异常来避免程序终结。
// 实现用来处理运行时异常的类,重写了uncaughtException()方法
class ExceptionHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.println("一个错误被捕获");
System.out.printf("Thread: %s\n",t.getId());
System.out.printf("Exception: %s: %s\n",e.getClass().getName(),e.getMessage());
}
}
public class ExceptionTest implements Runnable {
public void run() {
int num = Integer.parseInt("A");
}
public static void main(String[] args) {
Thread thread = new Thread(new ExceptionTest());
thread.setUncaughtExceptionHandler(new ExceptionHandler());
thread.start();
System.out.println("如果你看到这行消息,说明上面的异常被自己定义的异常处理捕获了,我就可以继续运行了~");
}
}
九、线程局部变量的使用
并发应用的一个关键地方就是共享数据。这个对那些扩展Thread类或者实现Runnable接口的对象特别重要。
如果你创建一个类对象,实现Runnable接口,然后多个Thread对象使用同样的Runnable对象,全部的线程都共享同样的属性。这意味着,如果你在一个线程里改变一个属性,全部的线程都会受到这个改变的影响。
有时你希望程序里的各个线程的属性不会被共享。 Java并发API提供了一个很清楚的机制叫本地线程变量。
http://ifeve.com/thread-management-10/
十、线程的分组
http://ifeve.com/thread-management-11/
十一、线程组中不可控异常的处理
http://ifeve.com/thread-management-12/
十二、使用工厂类创建线程
http://ifeve.com/thread-management-13/
public class MyThreadFactory implements ThreadFactory {
private int counter;
private String name;
private List<String> status;
public MyThreadFactory(String name){
counter=0;
this.name=name;
status=new ArrayList<String>();
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r,name+"-Thread_"+counter);
counter++;
status.add(String.format("Created thread %d with name %s on %s\n",t.getId(),t.getName(),new Date()));
return t;
}
public String getStatus() {
StringBuffer buffer = new StringBuffer();
Iterator<String> it = status.iterator();
while (it.hasNext()){
buffer.append(it.next());
buffer.append("\n");
}
return buffer.toString();
}
public static void main(String[] args) {
MyThreadFactory myThreadFactory = new MyThreadFactory("MyThreadFactory");
Task task = new Task();
Thread thread;
System.out.println("开始进程...");
for(int i=0;i<10;i++){
thread=myThreadFactory.newThread(task);
thread.start();
}
System.out.println("Factory Status:\n"+myThreadFactory.getStatus());
}
}
class Task implements Runnable{
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}