JAVA多线程四种实现方式
摘要:
java实现多线程有四种方式:一,通过继承Thread类;二,通过实现Runnable接口;三,通过Callable和FutureTask创建线程;四,通过线程池创建线程。其中继承Thread类和实现Runnable接口这两种方式无法返回结果,因为run方法的返回类型是void;如果我们想在任务执行完成后就可以获取执行结果,界的考虑使用Callable和Future构建的线程
Java多线程的实现
在java中实现多线程,有四种方式:
一,通过继承Thread类,重写run方法;
二,通过实现Runnable接口,重写run方法;
三,通过Callable和FutureTask创建线程;
四,通过线程池创建线程。
其中继承Thread类和实现Runnable接口这两种方式(Thread类本身也是Runnable的实现)无法返回结果,因为run方法的返回类型是void;如果我们想在任务执行完成后就可以获取执行结果,界的考虑使用Callable和Future构建的线程。
一,继承Thread类,重写run()方法,实现多线程
/**
* @author :Veiking
* @version :
* @version : 说明 :继承Thread类,实现多线程
*/
public class MyThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 30; i++) {
System.out.println("MainThread " + Thread.currentThread().getName() + "\t " + i);
if (i % 5 == 0) {
Thread myThreadA = new MyThread(); // 创建新的线程,线程进入新建状态
Thread myThreadB = new MyThread();
myThreadA.start(); // 调用start()方法,线程进入就绪状态
myThreadB.start();
}
}
}
}
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + "\t " + i);
}
}
}
从代码中可以看到,通过继承Thread类,重写run() 方法定义了一个新的线程类MyThread,其中run() 方法的方法体填充的是线程具体的业务,可以称之为线程执行体。
当这个 MyThread 类 new 出对象时,既可以创建一个新建状态的线程。然后通过调用这个对象的 start() 方法,这时候该线程就进入到就绪状态;就绪状态的线程并不一定会马上执行,具体取决于CPU的调度。
看源码我们可以了解到,Thread类其实也是实现了Runnable接口。
二,实现Runnable接口,重写run()方法,实现多线程
/**
* @author :Veiking
* @version :
* 说明 :实现Runnable接口,重写run()方法,实现多线程
*/
public class MyRunnableTest {
public static void main(String[] args) {
for (int i = 0; i < 30; i++) {
System.out.println("MainThread " + Thread.currentThread().getName() + "\t " + i);
if (i % 5 == 0) {
Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
Thread myThreadA = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
Thread myThreadB = new Thread(myRunnable);
myThreadA.start(); // 调用start()方法,线程进入就绪状态
myThreadB.start();
}
}
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + "\t " + i);
}
}
}
上面的代码,我们创建了Runnable实现类的实例对象,并以此对象作为Thread类的target,然后创建Thread对象,这个Thread对象才是真正的线程对象。
三,通过使用Callable接口和FutureTask类,实现多线程
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author :Veiking
* @version :
* 说明 :通过使用Callable和Future接口,实现多线程
*/
public class MyCallableTest {
public static void main(String[] args) {
Callable myCallable = new MyCallable(); // 创建 MyCallable 实例对象
FutureTask futureTask = new FutureTask(myCallable); // 将 myCallable 对象作为参数传递给 FutureTask 进行初始化
for (int i = 0; i < 15; i++) {
System.out.println("MainThread " + Thread.currentThread().getName() + "\t " + i);
if (i == 2) {
System.out.println("New Thread ...");
Thread thread = new Thread(futureTask); // 用 futureTask 对象作为 Thread 对象的 target 创建新的线程
thread.start(); // 调用start()方法,线程进入就绪状态
}
}
System.out.println("MainThread for end ...");
try {
int sum = futureTask.get(); // 调用 futureTask 对象的 get() 方法获取子线程执行的返回值
System.out.println("myCallable thread sum = " + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
/**
* @author :Veiking
* 说明 :创建Callable接口的实现类 ,并实现Call方法
*
*/
class MyCallable implements Callable {
// 与run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
int sum = 0;
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + "\t " + i);
sum += i;
}
return sum;
}
}
我们看看这段代码运行的结果:
根据代码我们可以看到,通过使用Callable和FutureTask接口,创建多线程,一般分为以下几个步骤:
1,创建Callable接口的实现类 ,并实现Call方法
2,创建 MyCallable 实例对象,然后将 myCallable 对象作为参数传递给 FutureTask 进行初始化
3,用 futureTask 对象作为 Thread 对象的 target 创建新的线程
4,调用 futureTask 对象的 get() 方法获取子线程执行的返回值
这里,根据源码了解到,FutureTask类实现了RunnableFuture接口,而RunnableFuture接口继承的是Runnable和Future这两个接口,也就是说线程方面还是通过Runnable实现的。我们又看了看FutureTask的 get() 方法,其本质是一个阻塞式的监听,通过判断当前task的状态为COMPLETING 且没有其他异常的情况下,返回获取值。
四,通过线程池,实现多线程
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author :Veiking
* @version :
* 说明 :通过线程池,实现多线程
*/
public class MyExecuteTest {
private static final int POOL_SIZE = 5; // 线程大小
private static final int POOL_NUM = 20; // 要启动的线程数量
public static void main(String[] args) {
// 初始线程池
ExecutorService executorService = Executors.newFixedThreadPool(POOL_SIZE);
for(int i = 0; i < POOL_NUM; i++) {
MyExecuteRunnable thread = new MyExecuteRunnable();
executorService.execute(thread);
}
//关闭线程池
executorService.shutdown();
}
}
class MyExecuteRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread By ThreadPool :" + Thread.currentThread().getName() + " ");
}
}
从代码可以看出,我们创建了一个容量为5的线程池,然后设置了20个线程,置入线程池去执行。这时候线程池里的总线程数量不会超过5,这里边其实是一个线程队列,执行完一个线程,然后再去队列里取一个执行。
很明显,这种线程池机制可以避免开启过多线程,导致资源的占用率过高,影响整体的性能。
查看源码了解到,Executors类使用了工厂模式的方法来创建线程池,这些返回的线程池都实现了ExecutorService接口,如图:
我们平时常用到的其实也就四种:
newFixedThreadPool(int nThreads); // 创建固定数目线程的线程池
newCachedThreadPool(); // 创建一个可缓存的线程池,即使用之前会判断下有没有可用线程缓存,空闲时也会一处超时过期的缓存线程
newSingleThreadExecutor(); // 创建一个单线程化的Executor
newScheduledThreadPool(int corePoolSize); // 创建一个支持定时任务的线程池,也可用来替代Timer类
* 额外补充,线程池使用使用Callable
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @author :Veiking
* @version :
* 说明 :通过线程池使用Callable和Future的方式
*/
public class MyCallableExecuteTest {
private static final int POOL_SIZE = 5; // 线程大小
private static final int POOL_NUM = 20; // 要启动的线程数量
public static void main(String[] args) {
// 初始线程池
ExecutorService executorService = Executors.newFixedThreadPool(POOL_SIZE);
Future future = executorService.submit(new MyExecuteCallable());
for(int i = 0; i {
// 与run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
int sum = 0;
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + "\t " + i);
sum += i;
}
return sum;
}
}
看源码的时候又看到这个submit() 方法了,就顺手试了下;通过submit()方法,可以传递一个Callable,返回Future对象,然后我们同样可以通过Future对象的get()方法,获取Callable实例中call()方法返回的值。
好了上面就是在 java 代码中实现多线程的四种方式。