【声明】:本篇文章来自本人gitee仓库搬运至CSDN,https://gitee.com/genmers/md-notes
Java线程学习记录
一、实现线程的两种方法
二、两种方法的本质
三、线程池实现算不算一种新的方式
四、参考资料
一、实现线程的两种方法
官方认证的两种实现线程的正确方法
- 方法一:实现Runnble接口
- 方法二:继承Thread类
两种方法的对比
方法1(实现Runnable接口)更好
假如我们重写的run方法都是这样
@Override
public void run() {
System.out.println("创建了一个线程");
}
1.从解耦的角度来看,run方法里的内容应该跟线程的创建运行的机制-也就是Thread类是解耦的
2.从资源消耗角度来看,如果用继承Thread类的方法,每次要使用线程都要新建一个独立的线程,而新建一个独立的线程(创建-执行-销毁)损耗的资源是比较大的
而使用Runnable,可以使用后续线程池之类的工具,可以减少消耗
3.由于Java不支持双继承,继承了Thread类就会导致这个类不能继承其他,大大限制了扩展性。
二、两种方法的本质
这是Thread类的run方法
/* What will be run. */
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
可以看到,Runnable方式的run方法是通过判断target是否为null,如果是runnable方式,target肯定不为空,就会执行target的run方法
而继承Thread类,子类重写run方法就会覆盖父类,就不会判断target,而是直接执行重写的方法
- 方法一:调用Target.run()
- 方法二:整个重写run方法
总结:
- 通常来说可以分为两类
- 准确的讲,创建线程只有一种方式就是构造Thread类,而实现线程的执行单元有两种方式
方法一:实现Runnable接口的run方法,并把Runnable实例传给Thread类
方法二:继承Thread类并重写run方法
三、线程池实现算不算一种新的方式
首先,我们知道线程池执行任务是使用execute方法,这个方法定义在Executor接口中
public interface Executor {
void execute(Runnable command);
}
这个方法是没有返回值的,只接受一个Runnable类型的参数
如果有接收返回值的需求怎么办
我在ExecutorService这个接口中发现了以下方法
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
可以看出,这个方法接收了一个Callable或者Runnable类型的参数,返回值是Future
我们先来看看Callable或者Runnable者两种接口
Callable
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
Runnable
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
可以看出
- Callable类型接收一个泛型参数,通过call方法返回一个该类型的值
- Runnable的run方法没有返回值
- Runnable虽然没有返回值,却可以调用submit(Runnable task, T result)自定义一个类型来接收结果
- Callable的call方法可以抛出异常,Runnable的run方法不行
Future
返回值Future也是一个接口, get是获取线程执行完任务返回值的方法
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
具体关于Callable和Future相关可查看参考资料
回到++线程池实现算不算一种新的方式++这个问题
package wrongways;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池方式实现线程
*/
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i=0;i<1000;i++){
executorService.submit(new Task(){
});
}
}
}
class Task implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
结果
pool-1-thread-8
pool-1-thread-12
pool-1-thread-4
pool-1-thread-9
pool-1-thread-13
pool-1-thread-15
pool-1-thread-5
...
pool-1-thread-999
pool-1-thread-1000
在Executors.java中搜索 new Thread,
/**
* The default thread factory
*/
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
从源码中我们可以看出线程池默认也是实现 ***ThreadFactory***接口,重写newThread方法,也是调用了Thread创建线程