Java线程具有并发性和异步性,可以说线程是轻量级别的进程,java中线程和现代操作系统中的进程调度都是采用采用抢占式运行。但线程和进程最大的区别是:一个进程中的多个进程共享这个进程的内存空间和系统资源,但是进程之间是有独立的代码段和数据段。
对于Callable配合ExecutorService使用创建线程请参考我的下一篇博文,实现的效果跟Callable配合FutureTask的效果大体相同。
下面介绍三种Java创建线程的方式:
1. 通过继承Thread类创建线程
2. 通过实现Runnable接口来创建线程
3. 通过实现Callable接口利用TaskFutrue来创建线程,当然也可以配合ExecutorServic来使用
接着说明下这三种方式创建线程的区别:
1.继承Thread类创建的线程可以拥有自己独立的类成员变量,但是实现Runnable接口创建线程共享实现接口类的成员变量。两中方式创建线程都要重写run方法,run方法是线程的执行体。
2.在继承Thread类创建进程中可以通过使用this获得当前进程的对象,但是在实现Runnable接口创建线程的途径中可以使用Thread.currentThread()方式来获得当前进程。
3.第三中方式是较为复杂的一种。Callable接口是一个与Runnable接口十分相似的接口。在Runnable中run方法为线程的执行体,但是在Callable接口中call方法是线程的执行体。下面是两个接口实现执行体的不同:
(1). call方法有返回值,但是run方法没有
(2). call方法可以生命抛出异常
所以可以说Callable接口是Runnable接口的增强版本
4.FutureTask类实现了Runnable和Future接口。和Callable一样都是泛型。
5.Future接口是对Callable任务的执行结果进行取消,查询是否完成,获取结果的。下面是这个接口的几个重要方法:
(1). boolean cancel(boolean myInterruptRunning)
试图取消Future与Callable关联的任务
(2). V get()
返回Callable任务中call方法的返回值,调用该方法会导致程序阻塞,必须等到子线程结束才会有
返回值。这里V表示泛型
(3). V get(long timeout, TimeUnit unit)
返回Callable中call方法的返回值,该方法让程序最多阻塞timeout毫秒的时间,或者直到unit时间
点。如果在指定的时间Callable的任务没有完成就会抛出异常TimeoutEexception
(4). boolean isCancelled()
如果Callable中的任务被取消,则返回true,否则返回false
(5). boolean isDone()
如果Callable中的任务被完成,则返回true,否则返回false
代码演示说明:
1. 通过继承Thread类创建线程
3.通过实现Callable接口利用TaskFutrue来创建线程
public class MyThread extends Thread {
private int count ; //每个新进程类都有独立的成员变量count
@Override
public void run() { //重写run方法
for (count = 1; count <= 5; count++) {
System.out.println(getName() + " :" + count);
}
}
public static void main(String[] agrs) {
for (int j = 1; j <= 10; j++) {
System.out.println(Thread.currentThread().getName()+" :"+j);
if(j == 5){
new MyThread().start();
new MyThread().start(); //俩个子线程和主线程并发执行
}
}
}
}
执行结果:(输出结果不唯一)每个子线程都输出5次,主线程输出10次,因子进程通过继承Thread类创建,拥有自己独立的成员变量count=5;main :1
main :2
main :3
main :4
main :5
main :6
Thread-0 :1
main :7
main :8
main :9
main :10
Thread-1 :1
Thread-0 :2
Thread-0 :3
Thread-0 :4
Thread-1 :2
Thread-0 :5
Thread-1 :3
Thread-1 :4
Thread-1 :5
2. 通过实现Runnable接口来创建线程
public class NewThread implements Runnable {
private int count; //用同一个NewThread类开辟的不同线程共享同一个类的成员变量
@Override
public void run() {
for(count = 1; count < 5; count++){
System.out.println(Thread.currentThread().getName() + " :" + count);
} //利用Thread.currentThread()获得当前进程
}
public static void main(String[] agrs){
NewThread nt = new NewThread();
for (int j = 1; j <= 10; j++) {
System.out.println(Thread.currentThread().getName()+" :"+j);
if(j == 5){
new Thread(nt).start();
new Thread(nt).start(); //俩个子线程和主线程并发执行
}
}
}
}
执行结果:(输出结果不唯一),通过同一个实现Runnable接口的类创建多个子线程共享该类的成员变量count = 5,所以两个子线程Thread-0 和Thread-1一共输出5次,主线程输出10次。
main :1
main :2
main :3
main :4
main :5
main :6
main :7
Thread-0 :1
main :8
Thread-1 :1
main :9
Thread-0 :2
main :10
Thread-1 :2
Thread-0 :3
Thread-1 :4
3.通过实现Callable接口利用TaskFutrue来创建线程
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class CallableThread implements Callable<String> {
private boolean flag;
public CallableThread(boolean f) {
this.flag = f;
}
@Override
public String call() throws Exception {
for (int i = 1; i <= 5; i++) {
//执行输出5次,因为call方式是阻塞方式的,所以这五次输出应该是连续
System.out.println(Thread.currentThread().getName() + " :" + i);
}
return flag == true ? "flag is true" : "flag is false";
}
}
public class FutureThread {
public static void main(String[] agrs) {
CallableThread ct1 = new CallableThread(false);
CallableThread ct2 = new CallableThread(true);
CallableThread ct3 = new CallableThread(false);
FutureTask<String> task1 = new FutureTask<String>(ct1);
FutureTask<String> task2 = new FutureTask<String>(ct2);
FutureTask<String> task3 = new FutureTask<String>(ct3);
Thread thread1 = new Thread(task1); //创建第一个线程
Thread thread2 = new Thread(task2); //创建第二个线程
Thread thread3 = new Thread(task3); //创建第三个线程
for(int i = 1; i <= 10; i++){
System.out.println(Thread.currentThread().getName()+" :"+i);
if(i == 5){
try{
thread1.start(); //启动第一个线程
System.out.println("task1 get value: "+task1.get());
thread2.start(); //启动第二个线程
System.out.println("task2 get value: "+task2.get());
thread3.start(); //启动第三个线程
System.out.println("task3 get value: "+task3.get());
}catch(Exception e){
e.printStackTrace();
}//每个线程的task调用get方法的时候都会阻塞程序,所以会连续输出5次后执行后面的程序
}
}
}
}
执行结果:(可能不唯一,但是Thread-0 :1到task3 get value: flag is false之间的输出是相同的,具体原因就是FutureTask的get()方法会使程序阻塞)
<span style="font-size:18px;">main :1
main :2
main :3
main :4
main :5
Thread-0 :1
Thread-0 :2
Thread-0 :3
Thread-0 :4
Thread-0 :5
task1 get value: flag is false
Thread-1 :1
Thread-1 :2
Thread-1 :3
Thread-1 :4
Thread-1 :5
task2 get value: flag is true
Thread-2 :1
Thread-2 :2
Thread-2 :3
Thread-2 :4
Thread-2 :5
task3 get value: flag is false
main :6
main :7
main :8
main :9
main :10</span>
对于Callable配合ExecutorService使用创建线程请参考我的下一篇博文,实现的效果跟Callable配合FutureTask的效果大体相同。