Java实现多线程的方式有比较多,但究其本质,最终都是在执行Thread的run方法,这个后文再作解释。下面先看看各种实现方式。
实现 Runnable 接口
public class RunnableThread implements Runnable{
@Override
public void run() {
System.out.println("Runnable接口实现线程");
}
public static void main(String[] args) {
new Thread(new RunnableThread()).start();
}
}
通过实现 Runnable 接口实现多线程,只需要重写run接口,然后将Runnable实例传递给Thread类,调用start即可启动线程。
继承 Thread 类
public class ExtendsThread extends Thread {
@Override
public void run() {
System.out.println("用Thread类实现线程");
}
public static void main(String[] args) {
new ExtendsThread().start();
}
}
继承 Thread 类实现多线程,这种方式直接重写Thread类的run方法,同样通过start方法启动多线程。
线程池创建线程
还可以通过线程池创建线程,方式参考如下
/**
* @author kangming.ning
* @date 2023-02-24 16:27
* @since 1.0
**/
public class CustomThreadPool1 {
private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setNamePrefix("线程池-").build();
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors() * 2,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) throws InterruptedException {
Runnable r = () -> {
System.out.println(Thread.currentThread().getName() + " is running");
};
for (int i = 0; i < 35; i++) {
Thread.sleep(1000);
threadPoolExecutor.submit(r);
}
}
}
当然,也可以使用ExecutorService去管理线程池,这个方式和上面的没本质区别,ExecutorService实际也是使用ThreadPoolExecutor创建的线程池,如下:
public class FixedThreadPool {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
for (int i = 0; i < 5; i++) {
executor.execute(new RunnableThread());
}
executor.shutdown();
}
}
public class RunnableThread implements Runnable {
@Override
public void run() {
System.out.println("用实现Runnable接口实现线程,ThreadName:" + Thread.currentThread().getName());
}
}
有返回值的 Callable 创建线程
public class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return new Random().nextInt();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//提交任务,并用Future提交返回结果
Future<Integer> future = service.submit(new CallableTask());
Integer randomNumber = future.get();//此方法阻塞
System.out.println("randomNumber:"+randomNumber);
service.shutdown();
}
}
定时器 Timer
public class TimerTest {
public static void main(String[] args) {
Timer timer=new Timer();
//延时0毫秒 每1秒执行一次
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("Timer线程,ThreadName:"+Thread.currentThread().getName());
}
},0,1000);
}
}
定时器Timer内部使用TimerThread处理定时任务,TimerThread继承于Thread类,于是,还是之前方式,只是包装了一下。
线程实现原理源码探索
通过上面的讲述,我们发现,看似有很多方式去实现多线程,但最终都需要通过Thread类,调用start方法,实现线程的开启。下面看一下Thread类的start方法
/* What will be run. */
private Runnable target;
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
@Override
public void run() {
if (target != null) {
target.run();
}
}
可以看出,start方法最终会经由start0本地方法最终调用run方法。而run方法里面的target是一个Runnable对象,如果target不是空,就调用target的run方法。这就解释了为什么实现Runnable接口传递给Thread构造函数,最终能调用Runnable的run方法。如果我们用继承Thread重写run方法来实现线程,明显,start方法调用的run方法直接就是这个子类的run方法了。这样看来,实现多线程只有一种方法,就是构造Thread类,调用start方法,经由本地方法start0最终调用它的run方法。
实现 Runnable 接口方式比继承 Thread 类方式好的理由
- Runnable 与 Thread 类的解耦,Thread 类负责线程启动和属性设置等内容,Runnable负责跑任务
- 可扩展 性更强,Java 语言不支持双继承,实现Runnable接口的类未来还可以继承别的类