实现多线程的方式
使用实现多线程有四种方式:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口,重写call()方法。然后包装成java.util.concurrent.FutureTask, 再然后包装成Thread
- (推荐)自定义线程池。这样线程的配置自己也能把控。
一、继承 Thread 类
public class Main {
public static void main(String[] args) {
//线程的调用入口,调用start方法
new MyThread().start();
}
}
class MyThread extends Thread {
//重写run方法
@Override
public void run() {
//实现自己的业务逻辑
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId());
}
}
优点:简单,且只需要实现父类的run方法即可(start方法中含有run方法,会创建一个新的线程,而run是执行当前线程)。
缺点:Java的单继承,如果对象已经继承了其他的类则不能使用该方法。且不能获取线程的返回值
二、实现Runnable 接口
public class Main {
public static void main(String[] args) {
// 将Runnable实现类作为Thread的构造参数传递到Thread类中,然后启动Thread类
MyRunnable runnable = new MyRunnable();
new Thread(runnable).start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId());
}
}
优点:简单,实现Runnable接口必须实现run方法。
缺点:创建一个线程就必须创建一个Runnable的实现类,且不能获取线程的返回值。
三、 实现Callable接口,重写call()方法。然后包装成java.util.concurrent.FutureTask, 再然后包装成Thread
public class Main {
public static void main(String[] args) throws Exception {
// 将Callable包装成FutureTask,FutureTask也是一种Runnable
MyCallable callable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
// get方法会阻塞调用的线程
Integer sum = futureTask.get();
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + "=" + sum);
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tstarting...");
int sum = 0;
for (int i = 0; i <= 100000; i++) {
sum += i;
}
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tover...");
return sum;
}
}
优点:可以获取多线程的返回值。
缺点:每个多线程都需要创建一个Callable的实现类
四、自定义线程池
public class Main {
public static void main(String[] args) throws Exception {
// 将Callable包装成FutureTask,FutureTask也是一种Runnable
MyCallable callable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
// get方法会阻塞调用的线程
Integer sum = futureTask.get();
System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + "=" + sum);
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tstarting...");
int sum = 0;
for (int i = 0; i <= 100000; i++) {
sum += i;
}
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tover...");
return sum;
}
}
优点:可以根据实际情况创建线程数量,且只需要创建一个线程池即可,也能够通过Callable和Future接口得到线程的返回值,程序的执行时间与线程的数量紧密相关。
缺点:需要手动销毁该线程池(调用shutdown方法)。