一、继承Thread
1、概述:Thread是Runnable的实现类,核心方法是run()方法
2、代码实现
public class ThreadModeCreate extends Thread {
@Override
public void run() {
System.out.println("执行了ThreadModeCreate的run方法");
throw new RuntimeException("异常");
}
}
//自定义线程异常捕获
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
// 处理异常的代码
System.out.println(t.getName()+"------"+e.getMessage());
}
}
class Test {
public static void main(String[] args) {
ThreadModeCreate threadModeCreate = new ThreadModeCreate();
threadModeCreate.setName("线程创建方式一");
MyUncaughtExceptionHandler handler = new MyUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(handler);
//执行run方法
threadModeCreate.start();
}
}
3、简化版本通过lambda表达式事项
new Thread(()->{
try {
//你要执行的方法
} catch (Exception e) {
e.printStackTrace();
}
},"AA").start();
二、实现Runnable接口
public class RunnableModeCreate implements Runnable
{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
class TestRunnable{
public static void main(String[] args) {
//方式一
RunnableModeCreate runnableModeCreate = new RunnableModeCreate();
Thread thread = new Thread(runnableModeCreate);
thread.start();
//方式二
new Thread(()->{
System.out.println(Thread.currentThread().getName());
}).start();
}
}
三、通过callable和Future创建线程
Future:一个接口表示异步执行某个操作
Callable:一个接口,可以通过自定义实现类,实现call()方法;在多线程执行时可以获取线程的执行结果
class m2 implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return Thread.currentThread().getName() + "通过Callable和Future";
}
}
public class CallableExe {
public static void main(String[] args) throws Exception {
//方法一
m2 m = new m2();
FutureTask<String> integerFutureTask = new FutureTask<String>(m);
new Thread(integerFutureTask, "Future").start();
//判断run方法是否执行完成
while (!integerFutureTask.isDone()) {
// System.out.println(Thread.currentThread().getName() + "…………wait");
}
String s = integerFutureTask.get();
System.out.println(s);
//方法二
//通过线程池实现
ExecutorService executorService = Executors.newFixedThreadPool(10);
//通过线程池执行该方法
Future<String> future = executorService.submit(m);
try {
System.out.println("获取参数");
//获取call的执行结果
String callback = future.get();
System.out.println(callback);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
//关闭线程池
executorService.shutdown();
}
}
}
四、通过线程池实现
优点:
1、避免方法调用导致线程对象的频繁创建,增加资源的消耗
2、可以对线程的数量进行监控和调优,避免线程数量创建过多,销毁不及时导致内存溢出
创建线程池的方法
- 通过创建ThreadPoolExecutor对象自定义线程池
- Executors.newScheduledThreadPool(10); 定时线程池
- Executors.newCachedThreadPool();可缓存线程池
- Executors.newFixedThreadPool(10); 定长线程池
- Executors.newSingleThreadExecutor(); 单线程化线程池
自定义线程池参数:
corePoolSize:存活核心线程数,不会回收;除非设置allowCoreThreadTimeOut为true;才会超时回收;
maximumPoolSize:线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
keepAliveTime:当线程数大于核心线程,这是多余空闲线程超过这个时间将被回收;
unit:线程存活的时间单位;枚举TimeUnit【MILLISECONDS(毫秒)、SECONDS(秒)】
workQueue:任务队列。通过线程池的 execute() 方法提交的 Runnable 对象没有线程执行,就储存在该队列中;
threadFactory:线程工厂。用于指定为线程池创建新线程的方式
handler:拒绝策略,超过线程边界和队列容量如何处理该数据
ArrayBlockingQueue delayQueue = new ArrayBlockingQueue(3);
ThreadPoolExecutor callerRunsPolicy = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MILLISECONDS, delayQueue);
callerRunsPolicy.execute(() -> {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
任务队列(workQueue)使用
1.ArrayBlockingQueue:数组阻塞队列
概述:一个由数组构成的有界阻塞队列;
实现:
ArrayBlockingQueue delayQueue = new ArrayBlockingQueue(2, true);
ThreadPoolExecutor callerRunsPolicy = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MILLISECONDS, delayQueue);
for (int i = 0; i < threadSize; i++) {
callerRunsPolicy.execute(this.runnableTest());
}
BlockingQueue<Runnable> queue = callerRunsPolicy.getQueue();
System.out.println(queue.size());
callerRunsPolicy.shutdown();
注意: 当需要运行的线程数大于最大核心数时,存储到该队列中,当队列装不下时看最大线程数,如果最大线程数大于核心线程数;开新线程;如何线程池和队列都装满了,就需要考虑拒绝策略了;
2.LinkedBlockingQueue:链表阻塞队列
概述:一个由链表构成的有界阻塞队列;默认最大值MAX_VALUE:2^31 -1
3.DelayQueue:延迟无界队列
概述:一个无界延迟队列;时间没到任务取不出来;要对象实现Delayed接口,自定compareTo和getDelay方法;
4.PriorityBlockingQueue:优先级阻塞队列
概述:一个支持优先级排序的无界阻塞队列,实现Comparable接口,重写compareTo方法,自定义优先级规则;
5.SynchronousQueue
概述:Executors.newCachedThreadPool()创建的线程池使用的队列默认是SynchronousQueue。该队列不存储数据;
注:在使用该队列的take()和put()方法时会出现阻塞,因为put添加的数据没有被take()消费,put就会阻塞等待消费,如take()消费方法执行队列没有数据,take会阻塞等待put添加数据;如果put和take在同一个线程中执行会出现死锁
new Thread(()->{
try {
System.out.println("开始添加数据");
queue.put(1);
System.out.println("put结束");
queue.take();
System.out.println("take结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
6.LinkedBlockingDeque
概述:基于链表创建的双向有界队列,默认边界是:最大值MAX_VALUE:2^31 -1
7.LinkedTransferQueue
概述:基于链表创建的无界阻塞队列,在线程池中作用等同于LinkedBlockingQueue
注: 有界队列和无界队列的区别;有界队列当队列饱和时,会执行最大线程池数;如何超过会执行拒绝策略;无界队列,任务队列永远都可以添加任务,所以maximumPoolSize 没有意义;
拒绝策略(handler)
当使用有界队列时,数据装不下,多余的数据就会走拒绝策略;jdk提供了以下四种拒绝策略;
1.AbortPolicy(默认):拒绝处理任务,并抛出异常RejectedExecutionHandler
2.CallerRunsPolicy:在调用线程中运行,除非该线程已关闭,在这种情况下,任务被丢弃
3.DiscardPolicy:直接丢弃不抛异常
4.DiscardOldestPolicy:丢弃队列最早的未处理任务(最先进入队列的任务),然后重新尝试执行任务。除非executor关闭,在这种情况下,任务将被丢弃
要想自己定义拒绝策略,实现RejectedExecutionHandler接口重写rejectedExecution()方法就行;
参考文献:https://blog.csdn.net/u013541140/article/details/95225769