继承Thread
1,定义Thread的子类,并重写该类的run方法,该run的方法的方法体就代表了线程要完成的任务。 2,创建Thread子类的实例,即创建了线程的对象。 3,调用线程对象的start()方法来启动该线程
package com.evan.springboot.study.thread;
/**
* @author evanYang
* @version 1.0
* @date 2022/04/23 09:55
*/
public class MyThread extends Thread {
public void run(){
System.out.println(this.getName()+" this is my thread");
}
public static void main(String[] args) {
System.out.println("this is main");
for (int i = 0; i < 50; i++) {
MyThread myThread = new MyThread();
myThread.run();
}
}
}
实现Runnable
1,定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。 2,创建runnable实例类的实例,并依赖此实例作为Thread的target来创建Thread对象,该Thread才是真正的线程对象 3,使用线程对象的start()方法来启动该线程
package com.evan.springboot.study.thread;
/**
* @author evanYang
* @version 1.0
* @date 2022/04/23 10:01
*/
public class MyRunable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 实现runable");
}
public static void main(String[] args) {
System.out.println("main is starting");
for (int i = 0; i < 10; i++) {
MyRunable myRunable = new MyRunable();
Thread thread = new Thread(myRunable);
thread.start();
}
}
}
main is starting
Thread-0 实现runable
Thread-1 实现runable
Thread-2 实现runable
Thread-4 实现runable
Thread-5 实现runable
Thread-6 实现runable
Thread-7 实现runable
Thread-9 实现runable
Thread-8 实现runable
Thread-3 实现runable
Process finished with exit code 0
实现Callable接口通过FutureTask包装器来创建Thread线程
1,创建Callable接口实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。 2,创建Callable实现类的实例,使用FutureTask类来包装Callable对象。该FutureTask对象封装了该Callable对象的call()方法的返回值。FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。 3,使用FutureTash对象作为Thread对象的target创建并启动新线程。 4,调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
package com.evan.springboot.study.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author evanYang
* @version 1.0
* @date 2022/04/23 10:08
*/
public class MyCallableThreadTest implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+ i);
}
return "return";
}
public static void main(String[] args) {
MyCallableThreadTest myCallableThreadTest = new MyCallableThreadTest();
FutureTask futureTask = new FutureTask<>(myCallableThreadTest);
new Thread(futureTask,"this is mycallable").start();
try {
Object o = futureTask.get();
System.out.println(o);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
创建线程的三种方式对比
- 1,采用实现Runnable、Callable接口的方式创建多线程时,
优势是: 1,线程只是实现了Runnable接口或Callable接口,还可以继承其他类。 2,在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个线程来处理同一份资源的情况,从而可以将cpu,代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。 劣势是: 1,编程会稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
- 2,使用继承Threda类的方式创建多线程优势是:
2.1编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势是:
2.2 线程类已经继承了Thread类,所以不能再继承其他父类。
通过线程池创建线程,使用线程池接口ExecutorService结合Callable、Future实现有返回结果的多线程
一个线程池包括以下四个基本组成: 1,线程池管理器ThreadPool:用于创建并管理线程包括 创建线程池,销毁线程池,添加新任务; 2,工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务。 3,任务接口(task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等; 4,任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制、
参数解释
- corePoolSize:核心线程数,
如果等于0,则任务执行完后,没有任务请求进入时销毁线程池中的线程,如果大于0,即使本地任务执行完毕,核心线程也不会销毁。设置过大会浪费资源,设置过小导致线程频繁创建。 - maximumPoolSize:最大线程数
必须大于等于1,且大于核心线程数。如果与corePoolSize相等,则线程池大小固定。如果大于corePoolSize,则最多创建maximumPoolSize执行任务。
TimeUnit表示时间单位。
当请求线程数大于corePoolSize时,线程进入BlockingQueue阻塞队列
拒绝策略
public class ThreadPoolExecutor extends AbstractExecutorService {
}
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
}
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
}
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
}
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
}
默认AbortPolicy()
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
DiscardPolicy
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
DiscardOldestPolicy
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
CallerRunsPolicy
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
submit()和execute()区别
execute
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
execute()方法的入参为一个Runnable,返回值为void,这时候我们已经知道了execute()方法的来源以及其定义
submit()
submit()是ExecutorService 接口中的,入参可以为Callable,也可以为Runnable,而且方法有返回值Future;
public interface ExecutorService extends Executor {
/**
* Submits a value-returning task for execution and returns a
* Future representing the pending results of the task. The
* Future's {@code get} method will return the task's result upon
* successful completion.
*
* <p>
* If you would like to immediately block waiting
* for a task, you can use constructions of the form
* {@code result = exec.submit(aCallable).get();}
*
* <p>Note: The {@link Executors} class includes a set of methods
* that can convert some other common closure-like objects,
* for example, {@link java.security.PrivilegedAction} to
* {@link Callable} form so they can be submitted.
*
* @param task the task to submit
* @param <T> the type of the task's result
* @return a Future representing pending completion of the task
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
* @throws NullPointerException if the task is null
*/
<T> Future<T> submit(Callable<T> task);
}
两者的区别
1,接收的参数不一样 2,submit()有返回值,而execute()没有 例如,有个validation的task,希望该task执行完后告诉我它的执行结果,是成功还是失败,然后继续下面的操作。 3,submit()可以进行Exception处理; 例如,如果task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过对Future.get()进行抛出异常的捕获,然后对其进行处理。
定长线程池 FixedThreadPool
固定数量的线程池,该线程池中的线程数量始终不变。 当有一个新任务提交时,线程池中若有空闲线程,则立即执行,若没有,则新的任务会被暂存到一个任务队列中,待有空闲线程在执行任务。 若任务队列满了,线程没有达到max线程数量,则会创建新的线程执行,直到达到最大线程。 若最大线程也创建完毕,则会走拒绝策略。
package com.evan.springboot.study.thread;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
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;
import java.util.concurrent.FutureTask;
/**
* @author evanYang
* @version 1.0
* @date 2022/04/23 15:27
*/
public class MyThreadPool {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 4; i++) {
int temp=i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("execute;"+Thread.currentThread().getName()+",i"+temp);
}
});
}
List<Future<String>> list = new ArrayList<Future<String>>();
for (int i = 0; i < 3; i++) {
Future<String> submit = fixedThreadPool.submit(new CallableWithResult(i));
list.add(submit);
}
for (Future<String> future : list) {
try {
System.out.println("future result is "+future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
class CallableWithResult implements Callable<String>{
private int id;
public CallableWithResult(int id){
id=this.id;
}
@Override
public String call() throws Exception {
System.out.println("call()方法被自动调用,干活!!! " + Thread.currentThread().getName());
// 下面的判读是模拟一个抛出异常的操作,随机得到一个true
if (new Random().nextBoolean())
throw new TaskException("Meet error in task." + Thread.currentThread().getName());
// 一个模拟耗时的操作
for (int i = 999999; i > 0; i--)
;
return "call()方法被自动调用,任务的结果是:" + id + " " + Thread.currentThread().getName();
}
}
class TaskException extends Exception{
public TaskException(String message){
super(message);
}
}
CachedThreadPool缓存线程池
如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
package com.evan.springboot.study.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author evanYang
* @version 1.0
* @date 2022/04/23 16:32
*/
public class MyCacheThreadPool {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
int tem=i;
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("threadName;"+Thread.currentThread().getName()+",i"+tem);
}
});
}
}
}
threadName;pool-1-thread-1,i0
threadName;pool-1-thread-6,i5
threadName;pool-1-thread-7,i6
threadName;pool-1-thread-4,i3
threadName;pool-1-thread-3,i2
threadName;pool-1-thread-2,i1
threadName;pool-1-thread-8,i7
threadName;pool-1-thread-5,i4
threadName;pool-1-thread-9,i8
threadName;pool-1-thread-10,i9
threadName;pool-1-thread-12,i11
threadName;pool-1-thread-11,i10
threadName;pool-1-thread-7,i12
threadName;pool-1-thread-2,i19
threadName;pool-1-thread-9,i16
threadName;pool-1-thread-8,i18
threadName;pool-1-thread-11,i13
threadName;pool-1-thread-10,i15
threadName;pool-1-thread-12,i14
threadName;pool-1-thread-5,i17
可以看到本来创建了20个线程池,这里只用了12个,因为newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
package com.evan.springboot.study.thread;
import lombok.SneakyThrows;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* @author evanYang
* @version 1.0
* @date 2022/04/23 16:37
*/
public class MySingleThreadPool {
public static void main(String[] args) {
ExecutorService sing = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index=i;
sing.execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println("index: "+index);
Thread.sleep(2000);
}
});
}
}
}
index: 0
index: 1
index: 2
index: 3
index: 4
index: 5
index: 6
index: 7
index: 8
index: 9
newScheduledThreadPool
支持定时及周期性的任务执行。
package com.evan.springboot.study.thread;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author evanYang
* @version 1.0
* @date 2022/04/23 16:42
*/
public class MyScheduledThredaPool {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 10; i++) {
final int tem=i;
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("i:" + tem);
}
},3, TimeUnit.SECONDS);
}
}
}
i:0
i:4
i:2
i:3
i:1
i:7
i:9
i:6
i:5
i:8
禁止直接使用Executors创建线程池原因:
Executors.newCachedThreadPool和Executors.newScheduledThreadPool两个方法最大线程数为Integer.MAX_VALUE,如果到达上线,没有任务服务器可以继续工作,肯定会抛出OOM异常。
Executors.newSingleThreadExecutor和Executors.newFixedThreadPool两个方法的workQueue参数newLinkedBlockingQueue(),容量为Integer.MAX_VALUE,如果瞬间请求非常大,会有OOM风险。
|