并发——Java 中创建线程的3种方法
一、3种方法
1.继承 Thread 类
(1)创建 Thread 类的子类
重写run()
,该方法就是线程的执行体。
(2)创建 Thread 继承类的实例
即创建了线程对象
(3)调用线程对象的start()
开启线程
public class Thread1 extends Thread {
@Override
public void run() {
super.run();
// 线程执行体
}
public static void main(String[] args) {
Thread t = new Thread();
t.start();
}
}
2. 实现 Runnable 接口
(1)创建 Runnable 接口的实现类
重写run()
作为线程执行体。
(2)创建 Runnable 实现类的实例
将该实例作为 Thread 的 target 传入创建 Thread 对象,该 Thread 类的实例是线程对象。
(3)调用线程对象的start()
开启线程
public class Thread2 {
private static class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行体
}
}
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
}
}
3. Callable
(1)Callback 和 FutureTask 相结合
-
创建
Callable
接口的实现类
重写call()
方法,为线程执行体。 -
用 FutureTask 包装 Callable 实现类的实例
FutureTask
实现了Runnable
接口。创建 Callable 实现类的实例,并用 FutureTask 包装,该FutureTask对象封装了该Callable对象的call()方法的返回值。 -
将 FutureTask 实例作为 Thread 的 target 创建 Thread 对象
该 Thread 对象为线程对象,通过start()
启动线程。 -
用 FutureTask 对象的
get()
方法来获得子线程执行结束后的返回值,调用get()方法会阻塞线程。
public class CallableTest {
/**
* Callable和FutureTask
* FutureTask::get()的阻塞性
*/
public static void main(String[] args) {
MyCallable myCallable = new MyCallable();
FutureTask < Integer > futureTask = new FutureTask(myCallable);
Thread t = new Thread(futureTask);
t.start();
// 会阻塞线程
try {
System.out.println(futureTask.get());
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static class MyCallable implements Callable < Integer > {
@Override
public Integer call() throws Exception {
return 0;
}
}
}
(2)Callable 和 Future 相结合
Java 并发编程
public class CallableTest {
/**
* Callable和Future
* Future::get()的阻塞性
* 在调用submit提交任务之后,主线程本来是继续运行了。但是运行到future.get()的时候就阻塞住了,一直等到任务执行完毕,拿到了返回的返回值,主线程才会继续运行。
*
* 这里注意一下,他的阻塞性是因为调用get()方法时,任务还没有执行完,所以会一直等到任务完成,形成了阻塞。
*
* 任务是在调用submit()方法时就开始执行了,如果在调用get()方法时,任务已经执行完毕,那么就不会造成阻塞。
* @param args
*/
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
MyCallable callable = new MyCallable();
Future < Integer > future = executorService.submit(callable);
try {
Integer res = future.get();
System.out.println(res);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
private static class MyCallable implements Callable < Integer > {
@Override
public Integer call() throws Exception {
return 0;
}
}
}
二、比较
Runnable
和Callable
(实现接口的方式)
-
优势
(1)还可继承其他类;(2)在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。 -
劣势
编程较麻烦,获取当前线程的方法Thread.getCurrentThread()
。
Thread
-
优势
编程简单;通过this
获取当前线程。 -
劣势
不可再继承其他类。