一.实现多线程
1.1实现多线程的方法到底是一种两种还是四种?
https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
PrimeThread p = new PrimeThread(143);
p.start();
The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method.
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
从官方文档可以看到,实现多线程的方法一共有两种,一是继承Thread类直接调用start()启动,
二是实现Runnable接口,将实例作为一个参数传递给一个thread实例,然后调用thread实例的start()启动
1.2哪种更好?
方法二实现Runnable去启动一个线程是更好的.
1.Java是单继承多实现,继承了Thread就无法继承其它类了,限制了可扩展性
2.如果只是单独启动一个线程不需要定制一些Thread方法,实现run方法就够用了,
不需要其它一些冗余的Thread方法,同时这些Thread方法如果只是调用也可以通过
接受这个实现runnable接口实例的thread实例调用
3.run方法代码里的内容对应具体的任务,应该与线程启动运行相关的东西解耦
1.3调用实质
两种方法调用实质
Thread
//继承重写,覆盖了原本的run方法
public void run() {
//重写内容
}
Runnable
//target就是传入的实现了Runnable的实例
public void run() {
if (target != null) {
target.run();
}
}
1.4同时使用两种方法会如何?
传入一个runnable实例并通过匿名内部类的方式重写thread类
new Thread(()->{
System.out.println("runnable");
}){
@Override
public void run() {
System.out.println("Thread");
}
}.start();
这时输出结果为"Thread",因为虽然传递了runnable实例,根据原本的Thread的run方法是会调用
target的run方法,但是现在Thread的run方法被重写了覆盖了原有逻辑,所以执行重写后的逻辑
1.5总结
启动一个线程通过thread的start(),这个方法内部存在一个start0(),start0()是一个native方法
执行OS创建启动线程的调用.
而这个被启动的线程运行的内容就是run()的代码
1.6问题的面试思路
通常分为两类,oracle对于Thread的api文档是如此解释的
准确的说,创建线程的方法只有通过thread类的start()方法,而实现线程执行内容的方法有两种方式
1.创建一个实现runnable接口的实例作为target传入thread实例,通过thread默认的执行逻辑调用target.run
2.重写thread类的run执行逻辑
1.7错误观点分析
1.7.1"线程池创建线程也算一种创建线程的方式"
源码分析
ExecutorService service = Executors.newCachedThreadPool();
service.execute(()->{
System.out.println("runnable");
});
public void execute(Runnable command) {
else if (!addWorker(command, false))
}
private boolean addWorker(Runnable firstTask, boolean core) {
w = new Worker(firstTask);
final Thread t = w.thread;
if (workerAdded) {
t.start();
workerStarted = true;
}
}
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
//通过this,将自身作为runnable实例传入
//Thread this worker is running in. Null if factory fails.
//worker运行在这个thread中
this.thread = getThreadFactory().newThread(this);
}
java.util.concurrent.ThreadPoolExecutor.Worker#run
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
//传入的runnable实例
Runnable task = w.firstTask;
task.run();
}
通过源码可以知道,这是通过重写thread的run方法替换执行逻辑,传入runnabe实例
作为task,然后调用task的run的方式
本质上依旧是通过传入实现runnable接口实例给thread实例创建线程的方式
1.7.2"通过Callable和FutureTask创建线程,也算是一种新建线程的方式"
通过源码可知
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
service.execute(() -> {
System.out.println("runnable");
});
//非静态内部类,需先实例化包裹它的类,再实例化它
CustomCallable callable = new createByPool().new CustomCallable();
Future<Integer> submit = service.submit(callable);
System.out.println(submit.get());
System.out.println("-------------------end---------------");
}
class CustomCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call");
return new Integer(500);
}
}
class CustomFutureTask extends FutureTask<Integer> {
public CustomFutureTask(Callable<Integer> callable) {
super(callable);
}
}
}
service.execute(()->{
System.out.println("runnable");
});
根据1.7.1可知运行了一个java.util.concurrent.ThreadPoolExecutor#runWorker
final void runWorker(Worker w) {
//通过getTask()取任务,然后调用
//getTask()取不到,在默认自旋时间后park(挂起)
while (task != null || (task = getTask()) != null)
task.run();
}
而service.submit(callable);
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
//(Callable最后还是包装为一个RunnableFuture
//并通过public void execute(Runnable command)
//调用
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
而FutureTask 实现了RunnableFuture<V>
public class FutureTask<V> implements RunnableFuture<V>
所以这两个依旧是通过传递一个runnable实例作为task,然后调用task的run的方式
至于返回值的提取,是根据一个本地变量,通过状态判断,完成后会直接返回或唤醒
然后返回这个本地变量
java.util.concurrent.FutureTask
private Object outcome;
public V get() throws InterruptedException, ExecutionException {
int s = state;
//如果state;没有COMPLETING)(完成)
if (s <= COMPLETING)
//挂起等待,执行完成后会唤醒
s = awaitDone(false, 0L);
return report(s);
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
1.7.3 定时器
public static void main(String[] args) {
Timer timer = new Timer();
//task工作任务
//delay延迟多久开始
//period两次执行之间的间隔
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println("TimerTask");
}
},1000,1000);
}
源码查看
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
sched(task, System.currentTimeMillis()+delay, period);
}
private void sched(TimerTask task, long time, long period) {
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
public Timer() {
this("Timer-" + serialNumber());
}
public Timer(String name) {
thread.setName(name);
//启动一个线程
thread.start();
}
//线程执行的代码
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}
private void mainLoop() {
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
task = queue.getMin();
if (taskFired = (executionTime<=currentTime))
if (taskFired)
task.run();
}
可以看到依旧是通过runnable实例作为task,然后调用task的run的方式;
1.7.4 匿名内部类和Lambda表达式的方式
这两者都只是一种语法,用来在不单独创建class类文件的情况下,
建立对某个接口的实现的类或者对某个类继承重写的类
匿名内部类可以重写多个方法,而Lambda需要目标是一个接口
且只有唯一一个待实现方法
1.8线程池执行过程分析
public void execute(Runnable command) {
//已经有运行的Worker则将command传入workQueue队列中
if (isRunning(c) && workQueue.offer(command))
//如果没有运行中的Worker会添加启动一个
else if (!addWorker(command, false))
}
//worker的run方法
java.util.concurrent.ThreadPoolExecutor.Worker#run
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
//通过getTask()取任务,然后调用
while (task != null || (task = getTask()) != null)
task.run();
}
//getTask()取不到值时
java.util.concurrent.SynchronousQueue.TransferStack#awaitFulfill
{ //超过自旋时间,会挂起进入等待状态
else if (nanos > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanos);
}
public static void parkNanos(Object blocker, long nanos) {
UNSAFE.park(false, nanos);
}
java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable, T)
java.util.concurrent.AbstractExecutorService#submit(java.util.concurrent.Callable<T>)
java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)
提交后都会调用一个execute(command)
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public void execute(Runnable command) {
if (isRunning(c) && workQueue.offer(command))
}
而workQueue.offer(command))则会唤醒这个同步队列
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
return transferer.transfer(e, true, 0) != null;
}
E transfer(E e, boolean timed, long nanos) {
m.tryMatch(s)
}
boolean tryMatch(SNode s) {
LockSupport.unpark(w);
}
然后执行
final void runWorker(Worker w) {
//getTask()恢复运行(unpark)
while (task != null || (task = getTask()) != null)
//有值后继续调用task.run
task.run();
}
1.9一个简单的手写线程池实现
线程池类
public class CustomThreadPool {
//task队列
private ConcurrentLinkedQueue<Runnable> queue = new ConcurrentLinkedQueue();
//工作线程集合
private Set<WorkThread> set = new HashSet<>();
//哪怕是线程本地变量,只要不是被这个线程自身修改,而是被其他
//线程修改,这种情况下依旧会因为缓存而无法识别修改,除非标注为volatile
//因为可被访问的线程本地变量也属于共有变量,存在线程安全问题
//volatile只适合于会被其它线程修改的基础变量,
//或者会被修改引用指向的引用变量.引用变量如果只会改变引用指向的对象的变量值
//不需要通过volatile修饰,如果会改变当前对象持有的引用变量的
//引用指向,则需要修饰;
private volatile boolean shutdown;
private AtomicInteger count;
public CustomThreadPool(int size) {
count = new AtomicInteger(size);
startThread(size);
}
//开启工作线程,并初始化集合
private void startThread(int num) {
new Random().ints().limit(num).forEach(it ->
{
WorkThread thread = new WorkThread();
set.add(thread);
thread.start();
}
);
}
//添加执行的command
public void execute(Runnable target) {
//如果已经关闭,直接返回
if (!shutdown)
synchronized (queue) {
if (queue.add(target)) queue.notify();
}
}
public int size() {
return set.size();
}
//当command执行完毕后退出
public synchronized void shutdown() {
shutdown = true;
set.stream().forEach(it -> {
it.shutdown();
if (it.getState().equals(Thread.State.WAITING)) {
synchronized (queue) {
queue.notifyAll();
}
}
});
clean();
}
//直接退出,不管任务有没执行完毕
public synchronized void shutdownNow() {
shutdown = true;
set.stream().forEach(this::shutdownNow0);
clean();
}
public void shutdownNow0(WorkThread thread) {
thread.shutdown();
thread.interrupt();
}
private void clean() {
try {
synchronized (CustomThreadPool.class) {
CustomThreadPool.class.wait();
Iterator<WorkThread> iterator = ((HashSet) set).iterator();
while (iterator.hasNext()) {
//Node<K,V> p = current;
//if (p == null)
iterator.next();
iterator.remove();
}
System.out.println("size>>>" + size());
queue = null;
set = null;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class WorkThread extends Thread {
Runnable target;
//哪怕是线程本地变量,只要不是被本地线程修改
//依旧会因为缓存而无法识别修改,除非标注为volatile
volatile boolean shutdown;
@Override
public void run() {
try {
while (true) {
if (Thread.currentThread().isInterrupted()) return;
Runnable command;
synchronized (queue) {
if ((command = target = get()) == null && !shutdown) {
queue.wait();
//从notifyAll唤醒后,依旧会争抢monitor的锁
//确保synchronize中同时只有一个线程可执行
continue;
}
}
//强逻辑确保指令有序性
if (run0(command) && shutdown) return;
}
} catch (InterruptedException e) {
return;
} catch (Exception e) {
e.printStackTrace();
} finally {
//确保clean时所有工作线程都已退出
//乐观锁实现,当要修改值时,通过比较发现值没变就替换,否则返回false,执行下一次循环
// public final int getAndAddInt(Object o, long offset, int delta) {
// int v;
// do {
// v = getIntVolatile(o, offset);
// } while (!compareAndSwapInt(o, offset, v, v + delta));
// return v;
// }
if (count.decrementAndGet() == 0)
synchronized (CustomThreadPool.class) {
System.out.println("clean");
CustomThreadPool.class.notify();
}
}
}
private boolean run0(Runnable command) {
if (command != null) command.run();
return true;
}
public void shutdown() {
this.shutdown = true;
}
public Runnable get() {
return queue.poll();
}
}
}
测试类
public static void main(String[] args) throws InterruptedException {
CustomThreadPool pool = new CustomThreadPool(10);
new Random().ints(5, 0, 5000).forEach(
it ->
new Thread(() -> {
try {
Thread.sleep(it);
pool.execute(() -> {
System.out.println("ok");
});
} catch (Exception e) {
e.printStackTrace();
}
}).start()
);
System.out.println("size>>>"+pool.size());
Thread.sleep(1000);
pool.shutdownNow();
// ThreadGroup group = new ThreadGroup("thread-pool");
// Thread thread = new Thread();
// thread.getThreadGroup().interrupt();
}
单次测试结果
size>>>10
ok
ok
clean
size>>>0
2.0错误观点总结
可以看到,这些各种各样的方法纠其本质,依旧是继承thread类,实现runnable接口,
而且大部分包装的方法都是通过传入一个runnable实例作为一个task,然后调用task的run的方式;
这也就是Thread线程默认run方法的调用方式,只是默认的run没那么复杂而已