一:进程线程的区别:
二,如何实现多线程
1.继承Thread类,重写run()方法
Thread本质是一个实现了Runnable接口的实例(Runnable接口后面会讲),代表的就是一个线程的实例。启动线程的的流程:首先通过Thread类的start()方法启动线程,这也是唯一的方法。start()方法将会启动一个新的线程,并执行run()方法,执行run方法中的代码。所以,我们一般会在run()方法中写入自己想要执行的代码。
示例代码:
public class Main {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
class MyThread extends Thread{ //创建一个线程
@Override
public void run() {
System.out.println("hello");
}
}
2.实现Runnable接口,并实现run()方法
主要步骤:
1)自定义类实现Runnable接口,实现run()方法
2)创建Thread对象,把实现Runnable接口的对象作为参数传入。
3)调用Thread的start()方法。
代码:
public class Main {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
class MyThread implements Runnable{ //创建一个线程
@Override
public void run() {
System.out.println("hello");
}
}
3.实现Callable接口,重写call()方法
Callable接口是Executor框架中的实现类。类似于Runnable接口,但是更加强大:
1)Callable接口在任务结束之后,可以返回一个返回值,Runnable则不可以。
2)Callable接口的call()方法可以跑出异常,但是Runnable接口的run()方法则不能抛出异常。
3)运行Callable接口可以拿到一个Future对象,Future对象表示异步计算的结果,提供了方法可以计算是否完成。因为多线程属于异步计算,前面的两种方法无法从别的线程得知返回情况。在这种方法们可以使用Future监视目标线程调用call()方法的情况,调用Future的get()方法以获取结果时,当前线程会被阻塞至call()方法结束返回结果。
代码:
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
//启动线程
Future<String> future = threadPool.submit(new CallableTest());
try{
System.out.println("wait...");
System.out.println(future.get());
System.out.println("finish...");
}catch(Exception e){
e.printStackTrace();
}
}
}
class CallableTest implements Callable<String>{
@Override
public String call() throws Exception {
return "hello";
}
}
总结:前两种方法,我们一般选择哪种方法?
第二种。看书中的介绍,主要是有两种原因:
一:(两种方法实现功能类似)Thread类定义多个方法可以被重写,但是只有run()方法必须被重写。Runnable接口也可以做到。
二:(默认的约定)一般只有一个类被加强、修改的时候,才会被继承。如果没有必要重写Thread类的其他方法,则可以通过实现Runnable接口就可以实现。因此一般选择第二种。
三:run()与start()区别:
通常调用start()方法,可以实现启动一个线程,并成为就绪态,但是并不是运行态。只是可以被JVM来调度。调度时,JVM调用run方法来完成实际的操作。因此,如果直接调用线程类的run方法,则变成一个普通的函数调用,依然是一个线程执行。如果调用start方法才能实现多线程。