进程与线程
进程是出于执行状态中的程序,是操作系统进行资源分配和调度的独立单位。
线程可以理解为是程序的一段执行流。显然一个进程必有一个线程,该线程称为主线程。同时Java提供有方法可以创建多个线程。
创建线程
Thread类
Thread类代表线程,Java中任何线程都必须是Thread类的子类的实例。
创建线程的一般步骤:
- 继承Thread类,重写run()方法
- 实例化Thread子类对象
- 调用Thread#start()方法
public class FirstThread extends Thread {
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(this.getName()+" "+i);//可以用this直接访问到当前线程实例
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);//用Thread类的静态方法访问当前线程实例
if(i==20){
//实例化Thread类,并调用start()方法
new FirstThread().start();
new FirstThread().start();
}
}
}
}
Runnable接口
Thread类有一个构造函数Thread(Runnable target),可以使用Runnable接口来创建线程:
public class SecondThread implements Runnable {
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20){
SecondThread secondThread = new SecondThread();
new Thread(secondThread,"线程1").start();
new Thread(secondThread,"线程2").start();
}
}
}
}
注意:用同一个Runnable实例创建了两个Thread,Runnable实现类中的成员对于这两个Thread是共享的。
Runnable是一个函数是接口,所以也可以使用lambda表达式:
public static void main(String[] args) {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20){
new Thread(() -> {
for (int j=0;j<100;j++){
System.out.println(Thread.currentThread().getName()+" "+j);
}
}).start();
}
}
}
Callable接口
如果想在线程的执行体中抛出异常或者返回值的话就必须用到Callable接口
- V call() throws Exception
通过实现这个方法就可以完成比run()更强大的工作。
但是Callable并不是Runnable的子类,所以不能直接传入Callable接口,需要借助一个FutureTask类来包装Callable,该类实现类Runnable接口,可以直接传入Thread构造函数。
public class ThirdThread implements Callable<String> {
@Override
public String call() throws Exception {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+""+i);
}
return "线程执行完毕";
}
public static void main(String[] args) {
//实例化FutureTask
FutureTask<String> futureTask = new FutureTask<>(new ThirdThread());
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+""+i);
if(i==20){
//传入
new Thread(futureTask).start();
}
}
try {
//主线程先阻塞,以便让副线程跑完
Thread.currentThread().sleep(1000);
//利用FutureRask的实例可以取出返回值
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
注意:一个FutureTask实例只可以创建一个线程,如果尝试创建多个线程只会有一个生效。
总结
直接继承Thread类的方法不常用,通常都是使用Runnable接口,如果需要返回值才考虑Callable。