Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程,
通过继承Thread类来创建并启动多线程的一般步骤如下:
1、定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2、创建Thread子类的实例,也就是创建了线程对象。
3、启动线程,即调用线程的start()方法。
public class MyThreadTest1 {
public static void main(String[] args) {
Thread thread1 = new MyThread1("线程1");
Thread thread2 = new MyThread2("线程2");
thread1.start();
thread2.start();
}
// //继承Thread类
static class MyThread1 extends Thread {
public MyThread1(String name) {
super(name);
}
// 重写run方法
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "-------第" + i + "次执行");
}
}
}
static class MyThread2 extends Thread {
public MyThread2(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "-------第" + i + "次执行");
}
}
}
}
通过实现Runnable接口创建并启动线程一般步骤如下:
1、定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体。
2、创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象。
3、通过调用线程对象的start()方法来启动线程。
public class MyThreadTest2 {
public static void main(String[] args) {
new Thread(new MyRunnable1(), "线程1").start();
new Thread(new MyRunnable2(), "线程2").start();
}
static class MyRunnable1 implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-------第" + i + "次执行");
}
}
}
static class MyRunnable2 implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-------第" + i + "次执行");
}
}
}
}
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大,call()方法可以有返回值,call()方法可以声明抛出异常。
执行 Callable 方式,需要 FutureTask实现类的支持,用于接收运算结果。FutureTask 是 Future 接口的实现类。
Callable接口是JAVA新增的接口,而且它不是Runnable接口的子接口,所以Callable对象不能直接作为Thread的target。还有一个原因就是:call()方法有返回值,call()方法不是直接调用,而是作为线程执行体被调用的,所以这里涉及获取call()方法返回值的问题。
于是,JAVA5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个FutureTask实现类,该类实现了Future接口,并实现了Runnable接口,所以FutureTask可以作为Thread类的target,同时也解决了Callable对象不能作为Thread类的target这一问题。
继承关系:
public class FutureTask<V> implements RunnableFuture<V> {
/* * Revision notes: This differs from previous versions of this * class that relied on AbstractQueuedSynchronizer, mainly to * avoid surprising users about retaining interrupt status during * cancellation races. Sync control in the current design relies * on a "state" field updated via CAS to track completion, along * with a simple Treiber stack to hold waiting threads. * * Style note: As usual, we bypass overhead of using * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics. */
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
/** * Sets this Future to the result of its computation * unless it has been cancelled. */
void run();
}
在Future接口里定义了几个公共方法来控制它关联的Callable任务:
public interface Future<V> {
/** * 视图取消该Future里面关联的Callable任务 */
boolean cancel(boolean mayInterruptIfRunning);
/** * 如果在Callable任务正常完成前被取消,返回True */
boolean isCancelled();
/** * 若Callable任务完成,返回True */
boolean isDone();
/** * 返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值 */
V get() throws InterruptedException, ExecutionException;
/** * 返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException */
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
介绍了相关的概念之后,创建并启动有返回值的线程的步骤如下:
public class MyCallableTest {
public static void main(String[] args){
// 2、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
// 使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable1());
// 3、使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
new Thread(futureTask).start();
Integer integer = null;
try {
// 4、调用FutureTask对象的get()方法来接受子线程执行结束后的返回值,所有的线程没有执行完成之后这里是不会执行的
integer = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(integer);
}
// 1、创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例。
static class MyCallable1 implements Callable<Integer> {
// 方法的返回值类型与上面Callable后面的泛型一致
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
}
return sum;
}
}
}
线程池提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提交了响应速度。线程池创建线程我们后面详细讲解,这里暂时不深入展开。
上面已经介绍完了JAVA中创建线程的三种方法,通过对比我们可以知道,JAVA实现多线程可以分为两类:一类是继承Thread类实现多线程;另一类是:通过实现Runnable接口或者Callable接口实现多线程。
下面我们来分析一下这两类实现多线程的方式的优劣:
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_44075963/article/details/113772758
内容来源于网络,如有侵权,请联系作者删除!