/Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home
Springboot注解
@RequestParam
@RequestBody
@RequestParam Long mailConfigId,
@RequestParam(required = false) String pt,
@RequestParam(required = false) Integer limit,
@RequestBody(required = false) Map<String, String> orderMap
异步注解@Async
同步(Synchronous):特指在同一个线程里面执行的内容之间的关系是有顺序的;线程之间需要同步时,就需要使用一些显式的同步方式,这也是多线程带来的大问题,在多线程编程需要特别关注。
异步(Asynchronous):特指一些执行内容可以不用直接等待运行结果,异步执行的内容一般是在另一个线程运行,只需要运行完结果反馈回来就行。
这个注解的作用在于可以让被标注的方法异步执行,但是有两个前提条件
-
配置类上添加
@EnableAsync
注解 -
需要异步执行的方法的所在类由Spring管理
-
需要异步执行的方法上添加了
@Async
注解
Demo
多线程与高并发
基本概念
进程
**进程:**每一个进程都占有 CPU、内存、磁盘、网络等资源。站在操作系统的角度,进程是分配资源的基本单位,也是最小单位。
从 CPU 角度,执行过程是这样子的:CPU 一直在负责执行指令,进程之间互相竞争 CPU 资源,下图有 A 和 B 进程,在一个时间点,CPU 只执行一个进程的指令,因为 CPU 运行很快,所以在咱们看起来,像是多个进程在同时跑。这就是进程带来的好处:提高资源利用率,并发地执行多个程序。
当然引入进程也不是有益无害,它增加了系统的时间和空间开销。空间开销这个好理解,进程有自己的组成部分(下面会讲),这个就占用了空间。时间开销则是进程切换需要时间。
串行(Serial)**、并行(Parallel)、**并发(Concurrent)
串行(Serial)
现在我们公司附近的快餐,人少的时候,就是排一条队,如下图所示,每个人按顺序排,一直往前走,如果看到想吃的菜,就用手指一指,快餐员工就会给你打菜,这个应该是很多快餐店都采用的方式,容易管理,但是有一点需要强调的就是如果一个同学只想吃米饭和豆芽,他还是需要排一整队到最后的结账台结账。这其实就是咱们计算机世界里面的串行,一条队伍,依次有序的执行着。
并行(Parallel)
不过一到 12 点高峰期上面那种排队方式就撑不住了,分分钟排队排到外面晒太阳,这时为了提高效率,因为快餐店还有一片空地,所以又加了一套打菜装备和员工,这时很好的解决了一条队伍太长的问题,这时就是并行了,2 套打菜装备和员工各自互不干涉,完全独立,每套装备能够解决每个顾客的点菜需求。当然这要求比较高,需要餐厅有足够的空间可以放下两套装备,并且需要雇佣多一倍的员工来为顾客打菜,这里就类似计算机的双核。
并发(Concurrent)
除了上面的两种快餐排队方式,还见过下面这种的,只有一套打菜装备,但是却有很多个队,每个菜排一条队(现实中其实不止一个菜,而是多个菜,这里画图就简化成一个菜),每个人就只需要排自己想吃的菜的队伍,这一道道菜就像计算机的各个资源,比如 CPU、IO 等等,人就像一个一个进程,有些只需要 CPU 计算资源,有些还要 IO 资源,各取所需,这种方式就是并发。这种打菜方式和上面第一种所需要的资源是一样的:一套打菜装备和员工。每个顾客最终都需要走到结账这个步骤,而结账只有一个,也就是类似计算机的单核。
总结
那么回归到计算机世界,这三者是什么东西呢?
早期计算机只有一个 CPU,也就是所谓的单核计算机,只有一个 CPU 去执行任务,所以只能是一个任务一个任务的跑,每个任务跑完才让下一个任务跑,也就是串行的。
后面因为进程和线程概念的提出与实现,它们使得任务以进程的方式运行,拆分成多个时间片运行,而不是一次运行执行完任务,这样子在一个 CPU 运行中可以有多个任务在并发执行,这时还是只有一个 CPU,一个时间点只有一个任务在执行。
而并行是因为 CPU 硬件的发展,出现多核 CPU,所以实现了真正的同一时间点能有多个任务在执行。
阻塞(Blocking)和非阻塞(Non-blocking)
如果某一个需求点阻塞了,应该就先做手头上其他工作,如果手头上没其他工作,就跟老板反馈情况后领其他任务做,还要时刻去跟进阻塞的需求点的进度。
阻塞
非阻塞
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YpV3zULU-1595785774605)(http://www.liebrother.com/upload/dd2c2cb241de4733afe6ad1610189194_02.jpg)]
线程
试想一下没有线程的程序是怎么样的?百度网盘在上传文件时就无法下载文件了,得等文件上传完成后才能下载文件。这个我们现在看起来很反人性,因为我们习惯了一个程序同时可以进行运行多个功能,而这些都是线程的功劳。
线程的组成
线程ID:线程标识符。
当前指令指针(PC):指向要执行的指令。
寄存器集合:存储单元寄存器的集合。
堆栈:暂时存放数据和地址,一般用来保护断点和现场。
线程与进程区别
线程和进程之间的区别,我觉得可以用这个例子来看出两者的不同,进程就是一栋房子,房子住着 3 个人,线程就是住在房子里的人。进程是一个独立的个体,有自己的资源,线程是在进程里的,多个线程共享着进程的资源。
线程状态
我们看到 Java 源代码里面,线程状态的枚举有如下 6 个。
public enum State {
//新建状态
NEW,
//运行状态
RUNNABLE,
//阻塞状态
BLOCKED,
//等待状态
WAITING,
//等待状态(区别在于这个有等待的时间)
TIMED_WAITING,
//终止状态
TERMINATED;
}
NEW:新建状态。在创建完 Thread ,还没执行 start() 之前,线程的状态一直是 NEW。可以说这个时候还没有真正的一个线程映射着,只是一个对象。
RUNNABLE:运行状态。线程对象调用 start() 之后,就进入 RUNNABLE 状态,该状态说明在 JVM 中有一个真实的线程存在。
BLOCKED:阻塞状态。线程在等待锁的释放,也就是等待获取 monitor 锁。
WAITING:等待状态。线程在这个状态的时候,不会被分配 CPU,而且需要被显示地唤醒,否则会一直等待下去。
TIMED_WAITING:超时等待状态。这个状态的线程也一样不会被分配 CPU,但是它不会无限等待下去,有时间限制,时间一到就停止等待。
TERMINATED:终止状态。线程执行完成结束,但不代表这个对象已经没有了,对象可能还是存在的,只是线程不存在了。
线程既然有这么多个状态,那肯定就有状态机,也就是在什么情况下 A 状态会变成 B 状态。下面就来简单描述一下。
结合下图,我们 new 出线程类的时候,就是 NEW
状态,
调用 start() 方法,就进入了 RUNNABLE
状态,
这时如果触发等待,则进入了 WAITING
状态,
如果触发超时等待,则进入 TIMED_WAITING
状态,
当访问需要同步的资源时,则只有一个线程能访问,其他线程就进入 BLOCKED
状态,
当线程执行完后,进入 TERMINATED
状态。
其实在 JVM 中,线程是有 9 个状态,如下所示,有兴趣的同学可以深入了解一下。
javaClasses.hpp
enum ThreadStatus {
NEW = 0,
RUNNABLE = JVMTI_THREAD_STATE_ALIVE + // runnable / running
JVMTI_THREAD_STATE_RUNNABLE,
SLEEPING = JVMTI_THREAD_STATE_ALIVE + // Thread.sleep()
JVMTI_THREAD_STATE_WAITING +
JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
JVMTI_THREAD_STATE_SLEEPING,
IN_OBJECT_WAIT = JVMTI_THREAD_STATE_ALIVE + // Object.wait()
JVMTI_THREAD_STATE_WAITING +
JVMTI_THREAD_STATE_WAITING_INDEFINITELY +
JVMTI_THREAD_STATE_IN_OBJECT_WAIT,
IN_OBJECT_WAIT_TIMED = JVMTI_THREAD_STATE_ALIVE + // Object.wait(long)
JVMTI_THREAD_STATE_WAITING +
JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
JVMTI_THREAD_STATE_IN_OBJECT_WAIT,
PARKED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park()
JVMTI_THREAD_STATE_WAITING +
JVMTI_THREAD_STATE_WAITING_INDEFINITELY +
JVMTI_THREAD_STATE_PARKED,
PARKED_TIMED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park(long)
JVMTI_THREAD_STATE_WAITING +
JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
JVMTI_THREAD_STATE_PARKED,
BLOCKED_ON_MONITOR_ENTER = JVMTI_THREAD_STATE_ALIVE + // (re-)entering a synchronization block
JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER,
TERMINATED = JVMTI_THREAD_STATE_TERMINATED
};
Java 线程实现
下面讲一讲在 Java 中如何创建一个线程。众所周知,实现 Java 线程有 2 种方式:继承 Thread 类和实现 Runnable 接口。
继承 Thread 类
继承 Thread 类,重写 run()
方法。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread");
}
}
实现 Runnable 接口
实现 Runnable 接口,实现 run()
方法。
class MyRunnable implements Runnable {
public void run() {
System.out.println("MyRunnable");
}
}
这 2 种线程的启动方式也不一样。
MyThread
是一个线程类,所以可以直接 new
出一个对象出来,接着调用 start()
方法来启动线程;
MyRunnable
只是一个普通的类,需要 new
出线程基类 Thread
对象,将 MyRunnable
对象传进去。
下面是启动线程的方式。
public class ThreadImpl {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread myRunnable = new Thread(new MyRunnable());
System.out.println("main Thread begin");
myThread.start();
myRunnable.start();
System.out.println("main Thread end");
}
}
打印结果如下:
main Thread begin
main Thread end
MyThread
MyRunnable
看这结果,不像咱们之前的串行执行依次打印,主线程不会等待子线程执行完。
敲重点:不能直接调用 run()
,直接调用 run()
不会创建线程,而是主线程直接执行 run()
的内容,相当于执行普通函数。这时就是串行执行的。看下面代码。
public class ThreadImpl {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread myRunnable = new Thread(new MyRunnable());
System.out.println("main Thread begin");
myThread.run();
myRunnable.run();
System.out.println("main Thread end");
}
}
打印结果:
main Thread begin
MyThread
MyRunnable
main Thread end
从结果看出只是串行的,但看不出没有线程,我们看下面例子来验证直接调用 run()
方法没有创建新的线程,使用 VisualVM 工具来观察线程情况。
我们对代码做一下修改,加上 Thread.sleep(1000000)
让它睡眠一段时间,这样方便用工具查看线程情况。
调用 run()
的代码:
public class ThreadImpl {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.setName("MyThread");
Thread myRunnable = new Thread(new MyRunnable());
myRunnable.setName("MyRunnable");
System.out.println("main Thread begin");
myThread.run();
myRunnable.run();
System.out.println("main Thread end");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyRunnable implements Runnable {
public void run() {
System.out.println("MyRunnable");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
main Thread begin
MyThread
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1mmwD1h4-1595785774608)(http://www.liebrother.com/upload/68ae1f24617f4cde8bf1f3382db17e42_dxc_0005_01.jpg)]
只打印出 2 句日志,观察线程时也只看到 main
线程,没有看到 MyThread
和 MyRunnable
线程,印证了上面咱们说的:直接调用 run()
方法,没有创建线程。
下面我们来看看有 调用 start()
的代码:
public class ThreadImpl {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.setName("MyThread");
Thread myRunnable = new Thread(new MyRunnable());
myRunnable.setName("MyRunnable");
System.out.println("main Thread begin");
myThread.start();
myRunnable.start();
System.out.println("main Thread end");
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
main Thread begin
main Thread end
MyThread
MyRunnable
所有日志都打印出来了,并且通过 VisualVM 工具可以看到 MyThread
和 MyRunnable
线程。看到了这个结果,切记创建线程要调用 start()
方法。
线程安全的停止
1、线程自然终止:自然执行完或抛出未处理异常
2、stop(),resume(),suspend()已不建议使用,stop()会导致线程不会正确释放资源,suspend()容易导致死锁。
3、使用interrupt()方法
java线程是协作式,而非抢占式
调用一个线程的interrupt() 方法中断一个线程,并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。
isInterrupted() 判定当前线程是否处于中断状态。
static方法interrupted() 判定当前线程是否处于中断状态,同时中断标志位改为false。
方法里如果抛出InterruptedException,线程的中断标志位会被复位成false,如果确实是需要中断线程,要求我们自己在catch语句块里再次调用interrupt()。
代码如下
import java.util.concurrent.ExecutionException;
/**
* @Auther: BlackKingW
* @Date: 2019/4/14 12:09
* @Description:
*/
public class DaemonThread {
private static class UseThread extends Thread {
@Override
public void run() {
try {
while (!isInterrupted()) {
System.out.println(Thread.currentThread().getName()
+ " I am extends Thread.");
}
System.out.println(Thread.currentThread().getName()
+ " interrupt flag is " + isInterrupted());
} finally {
System.out.println("...........finally");
}
}
}
public static void main(String[] args) throws InterruptedException,
ExecutionException {
UseThread useThread = new UseThread();
useThread.start();
Thread.sleep(5);
useThread.interrupt();
}
}
线程间的共享
1、synchronized内置锁
对象锁,锁的是类的对象实例。
类锁 ,锁的是每个类的的Class对象,每个类的的Class对象在一个虚拟机中只有一个,所以类锁也只有一个。
2、volatile关键字
适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。
3、ThreadLocal
线程变量。可以理解为是个map,类型 Map<Thread,Integer>
线程间协作
1、轮询
难以保证及时性,资源开销很大,
2、等待和通知
wait() 对象上的方法,将是当前执行线程进行等待。
notify/notifyAll 对象上的方法 发送信号量,唤醒线程。
代码示例:
git@github.com:nignmengcc/spring_boot_demo.git
TestWN
/**
* @author didi
*/
public class Express {
public final static String CITY = "ShangHai";
/**快递运输里程数*/
private int km;
/**快递到达地点*/
private String site;
public Express() {
}
public Express(int km, String site) {
this.km = km;
this.site = site;
}
/** 变化公里数,然后通知处于wait状态并需要处理公里数的线程进行业务处理
* */
public synchronized void changeKm(Integer km){
this.km = km;
notifyAll();
//其他的业务代码
}
/** 变化地点,然后通知处于wait状态并需要处理地点的线程进行业务处理*/
public synchronized void changeSite(String site){
this.site = site;
notify();
}
public synchronized void waitKm(){
while(this.km<=100) {
try {
wait();
System.out.println("check km thread["+Thread.currentThread().getId()
+"] is be notifed.");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("the km is"+this.km+",I will change db.");
}
public synchronized void waitSite(){
while(CITY.equals(this.site)) {
try {
wait();
System.out.println("check site thread["+Thread.currentThread().getId()
+"] is be notifed.");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("the site is"+this.site+",I will call user.");
}
}
/**
* @author didi
*/
public class TestWN {
/**
* 静态类express
* */
private static Express express = new Express(0,Express.CITY);
/**检查里程数变化的线程,不满足条件,线程一直等待*/
private static class CheckKm extends Thread{
@Override
public void run() {
// express.waitKm();
synchronized (express) {
while(express.getKm()<=100) {
try {
express.wait();
System.out.println("check km thread["+Thread.currentThread().getId()
+"] is waiting be notifed.");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("the km is"+express.getKm()+",I will change db.");
}
}
}
/**检查地点变化的线程,不满足条件,线程一直等待*/
private static class CheckSite extends Thread{
@Override
public void run() {
express.waitSite();
}
}
public static void main(String[] args) throws InterruptedException {
/**三个检测地点等待线程*/
for(int i=0;i<3;i++){
new CheckSite().start();
}
/**三个检测里程数等待线程*/
for(int i=0;i<3;i++){
new CheckKm().start();
}
Thread.sleep(1000);
/**快递里程变化*/
// express.changeKm(1000);
synchronized (express) {
express.setKm(1000);
express.notifyAll();
}
/**快递里程变化*/
express.changeSite("HangZhou");
}
}
等待和通知的标准范式
等待方:
- 获取对象的锁;
- 判断条件是否满足,不满足调用wait方法,继续等待
- 条件满足执行业务逻辑
通知方来说
- 获取对象的锁;
- 改变条件
- 通知所有在等待对象的线程
notify和notifyAll应该用谁?
应该尽量使用notifyAll,使用notify因为有可能发生信号丢失的的情况
notify()方法只能随机唤醒一个线程。
那么顾名思义,notifyAll()就是用来唤醒正在等待状态中的所有线程的,不过也需要注意以下几点:
(1)notifyAll()只会唤醒那些等待抢占指定object’s monitor的线程,其他线程则不会被唤醒。
(2)notifyAll()只会一个一个的唤醒,而并非统一唤醒。因为在同一时间内,只有一个线程能够持有object’s monitor
(3)notifyAll()只是随机的唤醒线程,并非有序唤醒。
3、join()方法
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
代码示例:
/**
* @author didi
*/
public class UseJoin {
static class JumpQueue implements Runnable {
private Thread thread;//用来插队的线程
public JumpQueue(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
try {
System.out.println(thread.getName()+" will be join before "
+Thread.currentThread().getName());
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" terminted.");
}
}
public static void main(String[] args) throws Exception {
//现在是主线程
Thread previous = Thread.currentThread();
for (int i = 0; i < 10; i++) {
//i=0,previous 是主线程,i=1;previous是i=0这个线程
Thread thread =
new Thread(new JumpQueue(previous), String.valueOf(i));
System.out.println(previous.getName()+" jump a queue the thread:"
+thread.getName());
thread.start();
previous = thread;
}
//让主线程休眠2秒
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " terminate.");
}
}
问题:调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?
线程在执行yield()以后,持有的锁是不释放的
sleep()方法被调用以后,持有的锁是不释放的
调动方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁
调动方法之前,必须要持有锁,调用notify()方法本身不会释放锁的
线程的并发工具类
一、Fork-Join框架
1、分而治之
规模为N的问题,N<阈值,直接解决,N>阈值,将N分解为K个小规模子问题,子问题互相对立,与原问题形式相同,将子问题的解合并得到原问题的解
动态规范
2、工作密取 workStealing
就是在任务分割的时候,前面的任务执行可能会比后面的执行速度快,当前面的执行完,后面的还没执行的时候,执行完前面的任务的线程不会停止,而是从后面的任务的尾部取出子任务继续工作。Fork-Join就是实现了这样的机制。
Fork/Join 使用的标准范式
二、常用的并发工具类
1、CountDownLatch
作用:是一组线程等待其他的线程完成工作以后在执行,加强版join
await用来等待,countDown负责计数器的减一
代码示例:
5、Callable**、Future****和FutureTask**
类之间的关系
isDone,结束,正常还是异常结束,或者自己取消,返回true;
isCancelled 任务完成前被取消,返回true;
cancel(boolean):
- 任务还没开始,返回false
- 任务已经启动,cancel(true),中断正在运行的任务,中断成功,返回true,cancel(false),不会去中断已经运行的任务
- 任务已经结束,返回false
线程池的使用与分析
一、什么是线程池?为什么要用线程池?
线程池(thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。
优势:
- 降低资源的消耗。降低线程创建和销毁的资源消耗;
- 提高响应速度。例如:线程的创建时间为T1,执行时间T2,销毁时间T3,免去T1和T3的时间
- 提高线程的可管理性。
二、如何实现一个线程池
根据线程池的概念,如果要自己创建线程池,应该满足一下条件。
- 保存线程的容器。因为线程必须在池子已经创建好了,并且可以保持住,因此,需要一个容器去保存我们的线程。
- 可以接受外部任务。线程还要能够接受外部的任务,冰并运行这个任务。
- 保存任务的容器,有些任务可能来不及执行,因此需要将来不及执行的任务通过容器保存起来。
根据以上的条件以及之前我们学的并发编程知识,我们先手动自己尝试写一个线程池
Code:
1、定义WorkThread类,用来表示执行的线程,用于监听阻塞队列任务。
2、创建构建函数,我们将线程池进行初始化,并启动所有的工作线程。workThreads用来保存运行的线程,使用BlockingQueue taskQueue用来保存我们的任务队列
3、创建提交任务方法execute,用于提交我们的任务。
4、创建销毁线程池的方法destroy,用于销毁线程池。
之后我们编写测试类
三、JDK中的线程池和工作机制
我们大致了解了线程池的一个机制,那我们看下JDK中,是如何实现线程池的吧。
JAVA中,ThreadPoolExecutor,是所有线程池实现的父类,类结构图如下。
它的构造函数含有以下参数:
参数 | 含义 |
---|---|
int corePoolSize | 线程池中核心线程数,< corePoolSize ,就会创建新线程,= corePoolSize ,这个任务就会保存到BlockingQueue,如果调用prestartAllCoreThreads()方法就会一次性的启动corePoolSize 个数的线程。 |
int maximumPoolSize | 允许的最大线程数,如果BlockingQueue也满了,并且线程数< maximumPoolSize时候就会再次创建新的线程 |
long keepAliveTime | 线程空闲下来后,存活的时间,这个参数只在线程数> corePoolSize才有用 |
TimeUnit unit | 存活时间的单位值 |
BlockingQueue workQueue | 保存任务的阻塞队列 |
ThreadFactory threadFactory | 创建线程的工厂,给新建的线程赋予名字 |
RejectedExecutionHandler handler | 饱和策略AbortPolicy :直接抛出异常,默认;CallerRunsPolicy:用调用者所在的线程来执行任务DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务DiscardPolicy :当前任务直接丢弃实现自己的饱和策略只要实现RejectedExecutionHandler接口即可 |
提交任务
execute(Runnable command) 不需要返回
Future submit(Callable task) 需要返回值
关闭线程池
shutdown(),shutdownNow();
shutdownNow():设置线程池的状态,还会尝试停止正在运行或者暂停任务的线程
shutdown()设置线程池的状态,只会中断所有没有执行任务的线程
2、线程池的工作机制
1、如果工作线程数小于核心线程数,则创建工作线程
2、如果工作线程数等于或者大于核心线程数,则将任务提交到阻塞队列中
3、如果阻塞队列也满了,但线程数小于最大线程数,则创建新的线程
4、如果创建新的线程也满了,则执行任务饱和策略。
源码如下
3、如何合理配置线程池
根据任务的性质来:计算密集型(CPU),IO密集型,混合型
计算密集型:例如加密,大数分解,正则……等
推荐:机器的Cpu核心数+1,为什么+1,防止页缺失,(机器的Cpu核心=Runtime.getRuntime().availableProcessors()😉
IO密集型:读取文件,数据库连接,网络通讯,
推荐:线程数适当大一点,机器的Cpu核心数*2,
混合型:尽量拆分,如果IO密集型远远计算密集型,拆分意义不大。
在阻塞队列的选择上,应该使用有界,无界队列可能会导致内存溢出
4、预定义的线程池
Java中,帮我们预定了5种线程池
1、FixedThreadPool
创建固定线程数量的线程池,适用于负载较重的服务器,使用了无界队列
2、SingleThreadExecutor
创建单个线程的线程池,适用于需要顺序保证执行任务,不会有多个线程业务,使用了无界队列
3、CachedThreadPool
会根据需要来创建新线程的,适用于执行很多短期异步任务的程序,使用了SynchronousQueue
4、WorkStealingPool(JDK7以后)
工作密取线程池,基于ForkJoinPool实现。适用于大任务分解的线程池。
5、ScheduledThreadPoolExecutor
需要定期执行周期任务的线程池。有两种实现
newSingleThreadScheduledExecutor:只包含一个线程,只需要单个线程执行周期任务,保证顺序的执行各个任务
newScheduledThreadPool 可以包含多个线程的,线程执行周期任务,适度控制后台线程数量的时候
方法说明:
schedule:只执行一次,任务还可以延时执行
scheduleAtFixedRate:提交固定时间间隔的任务
scheduleWithFixedDelay:提交固定延时间隔执行的任务
具体使用,大家可以自行百度,都比较多,一般如果并发比较高的业务中,以上线程池都不建议使用,因为他们采用的都是无界队列,任务量比较大的时候有可能导致内存溢出。一般正确用法是直接使用ThreadPoolExecutor进行创建,或根据自身需求进行自定义。
Function应用
Interface Function<T,R>
参数类型
T
- 函数输入的类型
R
- 函数的结果类型
可用lambda表达式或重写Apply方法实现Function。
实现一个Function
主要重写Apply方法,一般使用lambda表达式。
低端写法:
@Test
void testFunction01() {
Function<Long,Integer> function = new Function<Long, Integer>() {
@Override
public Integer apply(Long aLong) {
return Integer.valueOf(aLong.toString());
}
};
Long zz = 2L;
Integer cc = function.apply(zz);
System.out.println(cc);
}
Lambda 之低端写法
@Test
void testFunction() {
Function<Long,Integer> function = (u) -> {
return Integer.valueOf(u.toString());
};
Long zz = 2L;
Integer cc = function.apply(zz);
System.out.println(cc);
}
Lambda 表达式:
@Test
void testFunction() {
Function<Long,Integer> function = u -> Integer.valueOf(u.toString());
Long zz = 2L;
Integer cc = function.apply(zz);
System.out.println(cc);
}
功能方法
Modifier and Type | Method and Description |
---|---|
default Function | andThen(Function after) 返回一个组合函数,首先将该函数应用于其输入,然后将 after 函数应用于结果。 |
R | apply(T t) 将此函数应用于给定的参数。 |
default Function | compose(Function before) 返回一个组合函数,首先将 before 函数应用于其输入,然后将此函数应用于结果。 |
static Function | identity() 返回一个总是返回其输入参数的函数。 |
apply执行函数
R apply(T t)
将参数t应用于此Function,并返回结果R
andThen组合函数
Function andThen(Function after)
default <V> Function<T,V> andThen(Function<? super R,? extends V> after)
给指定Function添加组合函数,添加的组合函数会后执行;
返回组装好的组合函数
异常
NullPointerException
- 如果after为null
compose组合函数
Function compose(Function before)
default <V> Function<V,R> compose(Function<? super V,? extends T> before)
给指定Function添加组合函数,添加的组合函数会先执行;
返回组装好的组合函数
异常
NullPointerException
- 如果before是空
identity静态方法
static <T> Function<T,T> identity()
返回一个总是返回其输入参数的函数。
-
参数类型
T
- 函数的输入和输出对象的类型 -
结果
一个总是返回其输入参数的函数
@Test
void testFunction() {
Function<String,Long> beforeFunction = text -> Long.valueOf(text);
Function<Long,Integer> function = aLong -> Integer.valueOf(aLong.toString());
Function<Integer,Double> afterFunction = text -> Double.valueOf(text);
String textBefore = "100";
Long longAfter = 100L;
//function组合beforeFunction函数,并返回新函数,注意参数类型书写正确
Function<String,Integer> composeFunction = function.compose(beforeFunction);
//function组合afterFunction函数,并返回新函数,注意参数类型书写正确
Function<Long,Double> andThenFunction = function.andThen(afterFunction);
Integer applyBefore = composeFunction.apply(textBefore);
Double applyAfter = andThenFunction.apply(longAfter);
Function<String,String> identityFunction = Function.identity();
identityFunction.apply("这是一个不变的结果");
System.out.println(identityFunction.apply("这是一个不变的结果"));
}
基于双冒号的应用
PeopleTypeCountBO::getAllCount
双冒号实际是一个实现了getAllCount()方法的Function。
Function<PeopleTypeCountBO, Integer> function = PeopleTypeCountBO::getAllCount;
Function<PeopleTypeCountBO, Integer> functionGetAllCount = peopleTypeCountBO1 -> peopleTypeCountBO1.getAllCount();
//以下二者结果相同
function.apply(peopleTypeCountBO);
functionGetAllCount.apply(peopleTypeCountBO);
判空或null相关
字符串
StringUtils.isNotBlank(pt)
StringUtils.isNotBlank(pt)
isNotEmpty :
判断某字符串是否非空
StringUtils.isNotEmpty(null) = false
StringUtils.isNotEmpty("") = false
StringUtils.isNotEmpty(" ") = true
StringUtils.isNotEmpty("bob") = true
isNotBlank:
判断某字符串是否不为空且长度不为0且不由空白符(whitespace)构成,
下面是示例:
StringUtils.isNotBlank(null) = false
StringUtils.isNotBlank("") = false
StringUtils.isNotBlank(" ") = false
StringUtils.isNotBlank("\t \n \f \r") = false
Collectors.groupingBy
集合
package org.apache.commons.collections;
CollectionUtils.isNotEmpty()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B9hyfXRV-1595785774623)(/Users/didi/Library/Application Support/typora-user-images/image-20200719133423509.png)]
判断字符串相等
最好使用Objects提供的方法,避免空指针异常!
Objects.equal
邮件发送类TopicReportMailServiceImpl解析
/**
* @author onealzhuyingjie
* @date 20191017 下午2:50
**/
@Slf4j
@Service
public class TopicReportMailServiceImpl implements TopicReportMailService {
@Resource
private SqlQueryService sqlQueryService;
@Resource
private DataMonitorService dataMonitorService;
@Resource
private MailConfigMapper mailConfigMapper;
@Resource
private MDataService mDataService;
@Resource
private ExecutorService commonThreadPool;
@Resource
private RateLimiter dataMonitorLimiter;
@Resource
private SystemConfigService systemConfigService;
private static volatile boolean flag = true;
private static final String TO_USERS_KEY = "to_users";
private static final String TABLE_COPY_TO_KEY = "copy_to";
public static final String DEFAULT_BCC_TO = "onealzhuyingjie,wangfengjin,zhuluxu";
public static final int QUERY_BATCH_SIZE = 6;
@Override
public void sendReport(MailConfig mailConfig, String pt, Map<String, String> orderMap, Integer limit) {
// 说明性文字由模版定义,to_users、表格内容由hive表读取
// 支持多源邮件
Table<String, String, List<LinkedHashMap<String, Object>>> dataSourceToUsersDataTable = HashBasedTable.create();
Splitter splitter = Splitter.on(SEPARATOR_COMMA);
Joiner joiner = Joiner.on(SEPARATOR_COMMA);
List<String> sourceList = splitter.splitToList(mailConfig.getDataSourceName());
for (String source : sourceList) {
SqlContext context = new SqlContext();
context.select(Measure.column("*")).from(source);
if (StringUtils.isNotBlank(pt)) {
context.where(WhereFilter.column("pt").eq(pt));
}
if (orderMap != null && StringUtils.isNotBlank(orderMap.get(source))) {
context.orderBy(OrderField.column(orderMap.get(source)));
}
if (limit != null && limit >= 0) {
context.limit(limit);
}
SqlQueryResultDto resultDto = sqlQueryService.query(context);
if (null == resultDto) {
continue;
// throw new BusinessException(BusinessCode.ERROR_SEND_REPORT, "resultDto is null");
}
List<LinkedHashMap<String, Object>> data = resultDto.getData();
Map<String, List<LinkedHashMap<String, Object>>> toUsersDataMap =
data.stream().collect(Collectors.groupingBy(p -> String.valueOf(p.get(TO_USERS_KEY))));
toUsersDataMap.forEach((k, v) -> {
if (StringUtils.isNotBlank(k)) {
dataSourceToUsersDataTable.put(source, k, v);
}
});
}
String ignoreJobKeyword = systemConfigService.get("IGNORE_JOB_KEYWORD");
String[] ignoreJobKeywordArray = StringUtils.split(ignoreJobKeyword, SEPARATOR_COMMA);
String specialUser = systemConfigService.get("SPECIAL_USERS_EMAIL");
String[] specialUsersArray = StringUtils.split(specialUser, SEPARATOR_COMMA);
Set<String> specialUsersSet=Sets.newHashSet(specialUsersArray);
String[] toUsersArray = dataSourceToUsersDataTable.columnKeySet().toArray(new String[]{});
//拿到user对应的copy
List<String> queryList = Lists.newArrayListWithExpectedSize(QUERY_BATCH_SIZE);
Map<String, String> managerMap = Maps.newHashMap();
for (int i = 1; i <= toUsersArray.length; i++) {
queryList.add(toUsersArray[i - 1]);
if (i % QUERY_BATCH_SIZE == 0 || i == toUsersArray.length) {
mDataService.getStaffInfoByLdapBatch(queryList).stream()
.filter(p -> {
if ("A".equals(p.getHrStatus())) {
//增加特殊发送需求
if(specialUsersSet.contains(p.getLdap())){
return true;
}
// 过滤关键职位描述
for (int j = 0; ignoreJobKeywordArray != null && j < ignoreJobKeywordArray.length; j++) {
if (StringUtils.isNotBlank(p.getJobcodeDesc()) && StringUtils.isNotBlank(ignoreJobKeywordArray[j]) && StringUtils.contains(p.getJobcodeDesc(), ignoreJobKeywordArray[j])) {
return false;
}
}
return true;
}
return false;
})
.forEach(p -> managerMap.put(p.getLdap(), p.getManagerLdap()));
queryList.clear();
}
}
List<MonitorNotifyDO.Content.EmailNotify> emailNotifyList = Lists.newArrayList();
dataSourceToUsersDataTable.columnMap().forEach((toUsers, dataSourceDataListMap) -> {
MonitorNotifyDO.Content.EmailNotify emailNotify = new MonitorNotifyDO.Content.EmailNotify();
emailNotify.setParams(Maps.newHashMap(dataSourceDataListMap));
List<String> list = splitter.splitToList(toUsers);
Set<String> filterToUsers = Sets.newHashSet();
Set<String> copyToSet=Sets.newHashSet();
if(StringUtils.isNotBlank(mailConfig.getCopyTo())){
Set<String> copyToSetM = Sets.newHashSet(mailConfig.getCopyTo().split(","));
copyToSet.addAll(copyToSetM);
}
//增加copy_to字段的信息
dataSourceDataListMap.forEach((k,v)->{
if(StringUtils.isNotBlank(String.valueOf(v.get(0).get(TABLE_COPY_TO_KEY)))){
Set<String> copyToSetC = Sets.newHashSet(String.valueOf(v.get(0).get(TABLE_COPY_TO_KEY)).split(","));
copyToSet.addAll(copyToSetC);
}
});
list.stream().filter(managerMap::containsKey).forEach(p->{
if (mailConfig.getIsToSupervisor() == 1) {
copyToSet.add(managerMap.get(p));
}
filterToUsers.add(p);
});
if (mailConfig.getIsTest() == 1) {
emailNotify.setToUsers(DEFAULT_BCC_TO);
} else {
emailNotify.setToUsers(joiner.join(filterToUsers));
emailNotify.setCopyTo(joiner.join(copyToSet));
}
emailNotify.setBccTo(DEFAULT_BCC_TO);
emailNotify.setTitle(mailConfig.getTopic());
emailNotify.setTemplate(mailConfig.getTemplateName());
emailNotifyList.add(emailNotify);
});
flag = true;
commonThreadPool.submit(() -> {
for (MonitorNotifyDO.Content.EmailNotify emailNotify : emailNotifyList) {
if (flag) {
try {
dataMonitorLimiter.acquire();
log.info("send:{}", JSON.toJSONString(emailNotify));
dataMonitorService.sendMail(emailNotify);
} catch (BusinessException be) {
log.error("send mail to {} failed!, reason:{}", emailNotify.getToUsers(), be.getMessage());
}
} else {
log.info("stop send mail!");
}
}
});
}
@Override
public void sendReport(Long mailConfigId, String pt, Map<String, String> orderMap, Integer limit) {
sendReport(mailConfigMapper.selectByPrimaryKey(mailConfigId), pt, orderMap, limit);
}
@Override
public void setMailConfig(MailConfig mailConfig) {
buildMailDatasource(mailConfig.getDataSourceName());
mailConfigMapper.insertSelective(mailConfig);
}
private void buildMailDatasource(String name) {
for (String source : Splitter.on(",").splitToList(name)) {
if (null == sqlQueryService.getDatasource(source)) {
BuildDataSourceDTO buildDataSourceDTO = new BuildDataSourceDTO();
buildDataSourceDTO.setDbName(META);
buildDataSourceDTO.setTableName(source);
buildDataSourceDTO.setName(source);
buildDataSourceDTO.setQueryEngine(QueryEngineEnum.PRESTO.getCode());
sqlQueryService.buildDatasource(buildDataSourceDTO);
}
}
}
@Override
public void updateMailConfig(MailConfig mailConfig) {
MailConfig oldMailConfig = mailConfigMapper.selectByPrimaryKey(mailConfig.getId());
if (StringUtils.isNotBlank(mailConfig.getDataSourceName()) && !StringUtils.equals(oldMailConfig.getDataSourceName(), mailConfig.getDataSourceName())) {
buildMailDatasource(mailConfig.getDataSourceName());
}
mailConfig.setCreateTime(oldMailConfig.getCreateTime());
mailConfig.setModifyTime(new Date());
mailConfigMapper.updateByPrimaryKeySelective(mailConfig);
}
@Override
public void sendReportMailBySchedule(Date date) {
MailConfigCriteria configCriteria = new MailConfigCriteria();
configCriteria.createCriteria().andIsEnableEqualTo(1);
List<MailConfig> mailConfigs = mailConfigMapper.selectByExample(configCriteria);
for (MailConfig mailConfig : mailConfigs) {
String schedulePt = MailReportPeriod.getSchedulePt(mailConfig.getPeriod(), date);
if (StringUtils.isNotBlank(schedulePt)) {
log.info("schedule config:{}", JSON.toJSONString(mailConfig));
sendReport(mailConfig, schedulePt, null, null);
}
}
}
@Override
public List<MailConfig> listConfig() {
return mailConfigMapper.selectByExample(new MailConfigCriteria());
}
@Override
public void control(boolean nflag) {
flag = nflag;
}
}
Set<Long> sdIdSet = projectStorageList.stream().map(ProjectStorage::getSdId).collect(Collectors.toSet());
Map<Long, String> sdTypeMap = projectStorageList.stream().collect(Collectors.toMap(ProjectStorage::getSdId, ProjectStorage::getType, (o, n) -> o));
// if(StringUtils.isNotBlank(maxCreateTime)&&StringUtils.isNotBlank(minCreateTime)){
// whereFilter.addFilter(WhereFilter
// .column("year")
// .eq(dpsYear)
// .and(WhereFilter.column("month").eq(dpsMonth))
// .and(WhereFilter.column("day").eq(dpsDay))
// .and(WhereFilter.column("data_type").eq("1"))
// .and(WhereFilter.column("status").eq("4"))
// .and(WhereFilter.column("is_scheduled").eq(0)));
// }
<insert id="insertBatch" keyProperty="id" parameterType="java.util.List" useGeneratedKeys="true">
insert into auth_review_process_item (
sub_process_id,
db_name,
`table_name`,
`column_name`,
package_name,
package_id,
auth_type,
last_access_time,
snapshot,
is_recycle,
es_doc_id
)
values
<foreach collection="list" item="item" separator=",">
(#{item.subProcessId,jdbcType=BIGINT},
#{item.dbName,jdbcType=VARCHAR},
#{item.tableName,jdbcType=VARCHAR},
#{item.columnName,jdbcType=VARCHAR},
#{item.packageName,jdbcType=VARCHAR},
#{item.packageId,jdbcType=BIGINT},
#{item.authType,jdbcType=VARCHAR},
#{item.lastAccessTime,jdbcType=BIGINT},
#{item.snapshot,jdbcType=VARCHAR},
#{item.isRecycle,jdbcType=INTEGER},
#{item.esDocId,jdbcType=VARCHAR})
</foreach>
</insert>
DpsProductCenterAuthHistoryLogMapper
select db_name from meta.dwd_hive_tables
curl -X POST \
'http://bigdata.xiaojukeji.com/data_map/hql/parser/column/input_columns' \
-H 'Content-Type: application/json' \
-H 'cache-control: no-cache' \
-H 'client: 123' \
-d '{
"hql": "select * from meta.dwd_hive_tables",
"currentDb": ""
}'
pathList
//分批次处理
int batchSize = 100;
int num = pathList.size() / batchSize + 1;
for (int i = 0; i < num; i++) {
int fromIndex = batchSize * i;
int toIndex = Math.min(fromIndex + batchSize, pathList.size());
if (fromIndex >= toIndex) {
break;
}
。。。。。
}
//分批次处理
List<DpsProductCenterAuthHistoryLog> dpsLogsBath=new ArrayList<>();
Integer batchSize=800;
for (int i = 0; i < dpsAuthHistoryLogs.size(); i=i+batchSize) {
for (int j = i; j < i+batchSize&&j < dpsAuthHistoryLogs.size(); j++) {
dpsLogsBath.add(dpsAuthHistoryLogs.get(j));
}
log.info(dpsLogsBath.toString());
dpsProductCenterAuthHistoryLogEXtMapper.insertBatch(dpsLogsBath);
totalSqlIndex++;
log.info("=============插入第"+totalSqlIndex+"批==========");
dpsLogsBath.clear();
}
//分批次处理
Integer batchSize=800;
int num = dpsAuthHistoryLogs.size() / batchSize + 1;
for (int i = 0; i < num; i++) {
int fromIndex = batchSize * i;
int toIndex = Math.min(fromIndex + batchSize, dpsAuthHistoryLogs.size());
if (fromIndex >= toIndex) {
break;
}
//
dpsLogsBath.addAll(dpsAuthHistoryLogs.subList(fromIndex,toIndex));
log.info(dpsLogsBath.toString());
dpsProductCenterAuthHistoryLogEXtMapper.insertBatch(dpsLogsBath);
totalSqlIndex++;
log.info("=============插入第"+totalSqlIndex+"批==========");
dpsLogsBath.clear();
}
分页查询
// public List<AuthBO> inavalableAuthList(Integer offset,Integer limit) {
// String pt = sqlQueryService.getDatasourceNewestPt("dw_inavalable_auth_recycle");
// SqlContext context = new SqlContext();
//
// context.select(Measure.column("*")
//
//
// )
// .from("dw_inavalable_auth_recycle")
// .where(WhereFilter.column("pt").eq(pt));
//
// if (offset != null && limit!=null) {
// context.offset(offset).limit(limit);
// }
// List<LinkedHashMap<String, Object>> data = sqlQueryService.query(context).getData();
// return data.stream().map(
// p -> {
// AuthBO authBO=new AuthBO();
// authBO.setAuthId((String) p.get("es_doc_id"));
// authBO.setAuthOwner((String) p.get("auth_owner"));
// authBO.setAuthOwnerCn((String) p.get("auth_owner_cn"));
// authBO.setAuthType((String) p.get("auth_type"));
// authBO.setDbName((String) p.get("db_name"));
// authBO.setTableName((String) p.get("table_name"));
// authBO.setColumnName((String) p.get("column_name"));
// authBO.setPackageId((Long)(p.get("package_id")));
// authBO.setPackageName((String) p.get("package_name"));
// authBO.setProjectCode((String) p.get("project_code"));
// return authBO;
// }
// ).collect(Collectors.toList());
// }
DeleteUselessServiceImpl
Arrays.stream(TableOwnerType.values()).filter(p->p.code.equals(tcode)).findAny().orElse(null);
es查询总结
PageQueryRequest
BaseV2RequestInterface:
public class AuthListRequestDTO extends PageQueryRequest implements BaseV2RequestInterface
substring
StringFormat.format("{}_{}", dbTableName, pt)
getAuthOriginResult
return data.stream().map(p -> {
//"es_doc_id"---->"auth_id"映射
Map<String, String> columnFunctionMap = ImmutableMap.<String,String>builder().put("es_doc_id", "auth_id").build();
return (AuthBO) transfer(p, AuthBO.class, columnFunctionMap);
System.currentTimeMillis()
Gson
list大小
List<String> queryList = Lists.newArrayListWithExpectedSize(QUERY_BATCH_SIZE);
for (int i = 1; i <= toUsersArray.length; i++) {...}
queryList.add(toUsersArray[i - 1]);
if (i % QUERY_BATCH_SIZE == 0 || i == toUsersArray.length) {...queryList.clear();...}
DataMonitorServiceImpl
MDataService
log.info("debug trace{} ", LogMessage.getTraceId());
线程池相关:
private ExecutorService commonThreadPool;
private RateLimiter dataMonitorLimiter;
if (isAsync) {
commonThreadPool.submit(() -> {
for (MonitorNotifyDO.Content.EmailNotify emailNotify : emailNotifyList) {
if (flag) {
try {
log.info("debug trace{} ", LogMessage.getTraceId());
dataMonitorLimiter.acquire();
log.info("send:{}", JSON.toJSONString(emailNotify));
dataMonitorService.sendMail(emailNotify);
} catch (BusinessException be) {
log.error("send mail to {} failed!, reason:{}", emailNotify.getToUsers(), be.getMessage());
}
} else {
log.info("stop send mail!");
}
}
});
}
mvn mybatis-generator:generate -Dmybatis.generator.tableNames=resign_asset
.stream().filter(p->{
return checkInavalableAuth(p.getAuthOwner());
}).collect(Collectors.toList())
parallelStream
HealthScoreConsumerConfiguration
AuthRevokeDTO
审核人--->查看最新代码hash值是否正确--->发布执行--->
发布流程:
- eng–>流水线–>触发流水线。
http://eng.xiaojukeji.com/group/39455/service/10481/pipeline
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0B0XKhCE-1595785774624)(/Users/didi/Library/Application Support/typora-user-images/image-20200611153355122.png)]
-
ddcloud—>选择机房(首先美东),选择集群–>发布更新–>弹性云本平台部署–>写工单,核对代码版本
http://ddcloud.xiaojukeji.com/static/index.html#/elasticCloud/stateful?ns=datadream.didi.com
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q8D60ZmL-1595785774625)(/Users/didi/Library/Application Support/typora-user-images/image-20200611153859077.png)]
impactFactor
“healthScore”:100, //健康分
“impactFactor”:0.10, // 健康分影响因子
[
{ "iUser":{
"deptId1": "100565",
"department1": "基础平台部",
"deptId2": "102832",
"department2": "数据平台部",
"deptId3": "105720",
"department3": "数据资产平台",
"account": "liluyang",//部门管理员
"name": "李炉阳",//中文名
},
"storage": "469177292550834",//t3部门总资产
"healthScore": 75,//t3部门健康分
"storageRank": 1,//t3部门资产排名
"healthRank": 1,//t3部门健康分排名
"impactFactorRank":3, //健康分影响因子排名
"total":40//总数
}
]
@Resource
private WeekManageInfoVO weekManageInfoVO;
/**
* @author zhuluxu
* "ldap": "onealzhuyingjie",//账号
* "name": "朱英杰",//中文名
* "healthScore":100, //健康分
* "impactFactor":1000, // 健康分影响因子
* "storage": "1",//存储总量(至少1024*1024*1024kb)
* "isBlack": false,//是否为黑榜人员
* "isRed": false//是否为红榜人员
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WeekDeptDetailVO {
private String ldap;
private String name;
private Integer healthScore;
private Long impactFactor;
private String storage;
private Boolean isBlack;
private Boolean isRed;
}
//时间类
Calendar calendar = Calendar.getInstance();
// 获取当前年
int year = calendar.get(Calendar.YEAR);
// 获取当前月
int month = calendar.get(Calendar.MONTH) + 1;
// 获取当前日
int day = calendar.get(Calendar.DATE);
// 获取当前小时
int hour = calendar.get(Calendar.HOUR_OF_DAY);
// 获取当前分钟
int minute = calendar.get(Calendar.MINUTE);
// 获取当前秒
int second = calendar.get(Calendar.SECOND);
// 获取当前是本周第几天
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
// 获取当前是本周第几天
int weekOfYear = calendar.get(Calendar.WEEK_OF_YEAR);
// 获取当前是本月第几天
int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
// 获取当前是本年第几天
int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
WeekReportBaseReqDTO
String pt=date;
if(!StringUtil.isNotBlank(pt)){
pt=sqlQueryService.getDatasourceNewestPt(WeekTableSource.WEEK_TABLE_SOURCE.getDeptSource());
}
String querySql="\n" +
"select dept_id3, dept_desc3\n" +
" from " +WeekTableSource.WEEK_TABLE_SOURCE.getLdapSource()+
" where pt=" +pt;
List<LinkedHashMap<String, Object>> weekAllDept;
try {
weekAllDept = prestoSqlMapper.customQuery(querySql);
}catch(Exception e) {
log.error("query [{}] failed! e:{}",querySql, e.getMessage());
return null;
}
if(!weekAllDept.isEmpty()){
weekAllDept.forEach(wdl->{
WeekDepartmentVO weekDepartmentVO=new WeekDepartmentVO();
weekDepartmentVO.setDeptId3((String)wdl.get("dept_id3"));
weekDepartmentVO.setDeptDesc3((String)wdl.get("dept_desc3"));
weekDepartmentVOS.add(weekDepartmentVO);
});
}
weekDepartmentVOS.forEach(w->{
// WeekReportBaseReqDTO
// weekReportService.getWeekDeptAllDetail()
});
集合相关
Maps.newHashMap();
Objects.equal
JSONObject.parseObject(httpResponseResult.getResponseBody(),);
JSONObject.parseObject(jsonuser,User.class)
environment.acceptsProfiles(DeployEnv.PROD)
List<E> synArrayList = Collections.synchronizedList(new ArrayList<E>());
Set<E> synHashSet = Collections.synchronizedSet(new HashSet<E>());
Map<K,V> synHashMap = Collections.synchronizedMap(new HashMap<K,V>());
commonThreadPool
ThreadPoolConfig
quartz定时任务框架
英 [kwɔːts]
官网:http://www.quartz-scheduler.org/
quartz.properties
http://www.quartz-scheduler.org/documentation/quartz-2.3.0/configuration/
Quartz的配置通常是通过使用属性文件以及StdSchedulerFactory(使用配置文件并实例化调度程序)来完成的。
默认情况下,StdSchedulerFactory从“当前工作目录”中加载名为“ quartz.properties”的属性文件。如果失败,那么将加载位于org / quartz包中(作为资源)的“ quartz.properties”文件。如果要使用这些默认值以外的文件,则必须定义系统属性“ org.quartz.properties”以指向所需的文件。
或者,您可以通过在StdSchedulerFactory上调用getScheduler()之前,通过调用initialize(xx)方法之一来显式初始化工厂。
将按名称创建指定JobStore,ThreadPool和其他SPI类的实例,然后通过调用等效的“ set”方法在实例上设置配置文件中为其指定的任何其他属性。例如,如果属性文件包含属性“ org.quartz.jobStore.myProp = 10”,则在实例化JobStore类之后,将在其上调用方法“ setMyProp()”。在调用属性的setter方法之前,需要执行对原始Java类型(int,long,float,double,boolean和String)的类型转换。
一个属性可以通过遵循 $@other.property.name
的约定指定值来引用另一个属性的值,例如,要引用调度程序的实例名称作为其他属性的值,则可以使用“ $ @ org
”。 quartz.scheduler.instanceName”。
主属性:
org.quartz.scheduler.instanceName
可以是任何字符串,并且该值对调度程序本身没有意义-而是当同一程序中使用多个实例时,它用作客户端代码区分调度程序的机制。如果使用群集功能,则必须对群集中的每个实例使用“逻辑上”相同的Scheduler相同的名称。
org.quartz.scheduler.instanceId
可以是任何字符串,但是对于所有工作的调度程序来说必须是唯一的,就像它们在集群中是相同的“逻辑”调度程序一样。如果希望为您生成ID,则可以将值“ AUTO”用作instanceId。如果希望该值来自系统属性“ org.quartz.scheduler.instanceId”,则为“ SYS_PROP”。
org.quartz.scheduler.instanceIdGenerator.class
仅当org.quartz.scheduler.instanceId设置为“ AUTO”时使用。默认为“ org.quartz.simpl.SimpleInstanceIdGenerator”,它根据主机名和时间戳生成实例ID。其他IntanceIdGenerator实现包括SystemPropertyInstanceIdGenerator(从系统属性“ org.quartz.scheduler.instanceId”获取实例ID)和HostnameInstanceIdGenerator(使用本地主机名(InetAddress.getLocalHost()。getHostName()))。您也可以实现InstanceIdGenerator接口自己。
org.quartz.scheduler.threadName
可以是Java线程的有效名称的任何String。如果未指定此属性,则线程将接收调度程序的名称(“ org.quartz.scheduler.instanceName”)以及附加的字符串“ _QuartzSchedulerThread”。
org.quartz.scheduler.makeSchedulerThreadDaemon
一个布尔值(“ true”或“ false”),它指定调度程序的主线程是否应该是守护程序线程。另请参见org.quartz.scheduler.makeSchedulerThreadDaemon属性,以调整SimpleThreadPool(如果这是您正在使用的线程池实现)(很可能是这种情况)。
org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer
一个布尔值(“ true”或“ false”),它指定Quartz产生的线程是否将继承初始化线程(用于初始化Quartz实例的线程)的上下文ClassLoader。这将影响Quartz主调度线程,JDBCJobStore的失火处理线程(如果使用JDBCJobStore),集群恢复线程(如果使用集群)和SimpleThreadPool中的线程(如果使用SimpleThreadPool)。将此值设置为“ true”可能有助于类加载,JNDI查找以及与在应用程序服务器中使用Quartz相关的其他问题。
org.quartz.scheduler.idleWaitTime
是当调度程序空闲时在重新查询可用触发器之前调度程序将等待的时间(以毫秒为单位)。通常,除非您正在使用XA事务,并且在延迟触发应立即触发的触发器方面遇到问题,否则不必“调整”此参数。不建议使用小于5000 ms的值,因为它将导致过多的数据库查询。小于1000的值不合法。
org.quartz.scheduler.dbFailureRetryInterval
是调度程序在检测到JobStore内(例如,与数据库的)连接断开时将在重试之间等待的时间(以毫秒为单位)。使用RamJobStore时,此参数显然没有什么意义。
org.quartz.scheduler.classLoadHelper.class
默认为最可靠的方法,即使用“ org.quartz.simpl.CascadingClassLoadHelper”类-该类依次使用所有其他ClassLoadHelper类,直到一个可用为止。尽管应用程序服务器中似乎发生了奇怪的事情,但您可能不应该发现需要为此属性指定任何其他类。当前所有可能的ClassLoadHelper实现都可以在org.quartz.simpl包中找到。
org.quartz.scheduler.jobFactory.class
要使用的JobFactory的类名。JobFatcory负责产生JobClasses的实例。默认值为“ org.quartz.simpl.PropertySettingJobFactory”,它每次执行即将执行时,仅在类上调用newInstance()来生成一个新实例。PropertySettingJobFactory还使用SchedulerContext和Job和Trigger JobDataMaps的内容来反射性地设置作业的bean属性。
org.quartz.context.key.SOME_KEY
表示将以字符串形式放置在“调度程序上下文”中的名称/值对。(请参阅Scheduler.getContext())。因此,例如,设置“ org.quartz.context.key.MyKey = MyValue”将执行scheduler.getContext()。put(“ MyKey”,“ MyValue”)的等效操作。
除非您使用JTA事务,否则应将与事务相关的属性保留在配置文件中。
org.quartz.scheduler.userTransactionURL
应该设置为Quartz可以在其中找到Application Server的UserTransaction管理器的JNDI URL。默认值(如果未指定)是“ java:comp / UserTransaction”-几乎适用于所有Application Server。Websphere用户可能需要将此属性设置为“ jta / usertransaction”。仅当Quartz配置为使用JobStoreCMT且org.quartz.scheduler.wrapJobExecutionInUserTransaction设置为true时,才使用此选项。
org.quartz.scheduler.wrapJobExecutionInUserTransaction
如果您希望Quartz在调用对您的作业执行execute之前启动UserTransaction,应将其设置为“ true”。在作业的execute方法完成之后,以及JobDataMap更新后(如果它是StatefulJob),Tx将提交。默认值为“ false”。您可能还对在作业类上使用*@ExecuteInJTATransaction*批注感兴趣,该批注可让您控制单个作业是否Quartz应该启动JTA事务-而此属性使它在所有作业中都发生。
org.quartz.scheduler.skipUpdateCheck
是否跳过运行快速Web请求以确定是否有可供下载的Quartz更新版本。如果检查运行,并且找到更新,它将在Quartz日志中报告为可用。您还可以使用系统属性“ org.terracotta.quartz.skipUpdateCheck = true”(可以在系统环境中设置或在Java命令行中将其设置为-D)来禁用更新检查。建议您禁用生产部署的更新检查。
org.quartz.scheduler.batchTriggerAcquisitionMaxCount
允许调度程序节点一次获取(触发)的最大触发器数。默认值为1。数字越大,触发效率就越高(在一次触发很多触发器的情况下)-但代价是群集节点之间的负载可能不均衡。如果此属性的值设置为> 1,并且使用JDBC JobStore,则必须将属性“ org.quartz.jobStore.acquireTriggersWithinLock”设置为“ true”,以避免数据损坏。
org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow
允许触发器被捕获并在其计划的触发时间之前触发的时间(以毫秒为单位)。
默认值为0。数字越大,触发触发器的批量采集越有可能一次选择并触发1个以上的触发器-代价是未精确遵守触发计划(触发器可能会提前触发该数量) )。在调度程序具有大量需要同时或接近同时触发的触发器的情况下,这可能很有用(出于性能考虑)。
配置ThreadPool设置
物业名称 | 需要 | 类型 | 默认值 |
---|---|---|---|
org.quartz.threadPool.class | 是 | 字符串(类名) | 空值 |
org.quartz.threadPool.threadCount | 是 | 整型 | -1 |
org.quartz.threadPool.threadPriority | 没有 | 整型 | Thread.NORM_PRIORITY(5) |
org.quartz.threadPool.class
是您要使用的ThreadPool实现的名称。Quartz附带的线程池是“ org.quartz.simpl.SimpleThreadPool”,并且应该满足几乎每个用户的需求。它的行为非常简单,并且经过了很好的测试。它提供了一个固定大小的线程池,这些线程池“活跃”了调度程序的生命周期。
org.quartz.threadPool.threadCount
可以是任何正整数,尽管您应该意识到只有1到100之间的数字是非常实用的。这是可用于并发执行作业的线程数。如果您每天只有少量工作要执行几次,那么一个线程就足够了!如果您有成千上万的工作,每分钟触发许多工作,那么您可能希望线程数更多,例如50或100(这在很大程度上取决于您工作执行的工作性质以及系统资源!)。
org.quartz.threadPool.threadPriority
可以是Thread.MIN_PRIORITY(为1)和Thread.MAX_PRIORITY(为10)之间的任何整数。默认值为Thread.NORM_PRIORITY(5)。
SimpleThreadPool特定的属性
物业名称 | 需要 | 类型 | 默认值 |
---|---|---|---|
org.quartz.threadPool.makeThreadsDaemons | 没有 | 布尔值 | 假 |
org.quartz.threadPool.threadsInheritGroupOfInitializingThread | 没有 | 布尔值 | 真正 |
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread | 没有 | 布尔值 | 假 |
org.quartz.threadPool.threadNamePrefix | 没有 | 串 | [计划者名称] _Worker |
org.quartz.threadPool.makeThreadsDaemons
可以设置为“ true”以将池中的线程创建为守护程序线程。默认值为“ false”。另请参见*org.quartz.scheduler.makeSchedulerThreadDaemon*属性。
org.quartz.threadPool.threadsInheritGroupOfInitializingThread
可以为“ true”或“ false”,默认为true。
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread
可以为“ true”或“ false”,默认为false。
org.quartz.threadPool.threadNamePrefix
工作池中线程名称的前缀-将附加一个数字。
自定义线程池
如果使用自己的线程池实现,则可以简单地通过如下命名属性来反射地设置属性:
在自定义线程池上设置属性
org.quartz.threadPool.class = com.mycompany.goo.FooThreadPool
org.quartz.threadPool.somePropOfFooThreadPool = someValue
配置JDBC-JobStoreTX
JDBCJobStore用于在关系数据库中存储调度信息(作业,触发器和日历)。实际上,可以根据您需要的事务行为在两个单独的JDBCJobStore类之间进行选择。
JobStoreTX通过在执行每个操作(例如添加作业)之后在数据库连接上调用commit()(或rollback())本身来管理所有事务。如果您在独立应用程序中使用Quartz,或者在Servlet容器中使用Quartz(如果应用程序未使用JTA事务),则JDBCJobStore是合适的。
通过将“ org.quartz.jobStore.class”属性设置为以下方式来选择JobStoreTX:
将计划程序的JobStore设置为JobStoreTX
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
可以使用以下属性调整JobStoreTX:
物业名称 | 需要 | 类型 | 默认值 |
---|---|---|---|
org.quartz.jobStore.driverDelegateClass | 是 | 串 | 空值 |
org.quartz.jobStore.dataSource | 是 | 串 | 空值 |
org.quartz.jobStore.tablePrefix | 没有 | 串 | “ QRTZ_” |
org.quartz.jobStore.useProperties | 没有 | 布尔值 | 假 |
org.quartz.jobStore.misfireThreshold | 没有 | 整型 | 60000 |
org.quartz.jobStore.isClustered | 没有 | 布尔值 | 假 |
org.quartz.jobStore.clusterCheckinInterval | 没有 | 长 | 15000 |
org.quartz.jobStore.maxMisfiresToHandleAtATime | 没有 | 整型 | 20 |
org.quartz.jobStore.dontSetAutoCommitFalse | 没有 | 布尔值 | 假 |
org.quartz.jobStore.selectWithLockSQL | 没有 | 串 | “选择*从{0} SCHED_NAME = {1}和LOCK_NAME =?的锁进行更新” |
org.quartz.jobStore.txIsolationLevelSerializable | 没有 | 布尔值 | 假 |
org.quartz.jobStore.acquireTriggersWithinLock | 没有 | 布尔值 | 假(或真-请参见下面的文档) |
org.quartz.jobStore.lockHandler.class | 没有 | 串 | 空值 |
org.quartz.jobStore.driverDelegateInitString | 没有 | 串 | 空值 |
org.quartz.jobStore.driverDelegateClass
驾驶员代表了解各种数据库系统的特定“方言”。可能的选择包括:
- org.quartz.impl.jdbcjobstore.StdJDBCDelegate(用于完全兼容JDBC的驱动程序)
- org.quartz.impl.jdbcjobstore.MSSQLDelegate(用于Microsoft SQL Server和Sybase)
- org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
- org.quartz.impl.jdbcjobstore.WebLogicDelegate(用于WebLogic驱动程序)
- org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
- org.quartz.impl.jdbcjobstore.oracle.WebLogicOracleDelegate(用于Weblogic中使用的Oracle驱动程序)
- org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate(用于Weblogic中使用的Oracle驱动程序)
- org.quartz.impl.jdbcjobstore.CloudscapeDelegate
- org.quartz.impl.jdbcjobstore.DB2v6Delegate
- org.quartz.impl.jdbcjobstore.DB2v7Delegate
- org.quartz.impl.jdbcjobstore.DB2v8Delegate
- org.quartz.impl.jdbcjobstore.HSQLDBDelegate
- org.quartz.impl.jdbcjobstore.PointbaseDelegate
- org.quartz.impl.jdbcjobstore.SybaseDelegate
请注意,已知许多数据库都可以与StdJDBCDelegate一起使用,而其他数据库则可以与其他数据库的委托一起使用,例如Derby与Cloudscape委托可以很好地配合(在此不足为奇)。
org.quartz.jobStore.dataSource
此属性的值必须是配置属性文件中定义的数据源之一的名称。有关更多信息,请参见DataSources的配置文档。
org.quartz.jobStore.tablePrefix
JDBCJobStore的“表前缀”属性是一个字符串,该字符串等于在数据库中创建的Quartz表的前缀。如果它们使用不同的表前缀,则可以在同一数据库中具有多组Quartz表。
org.quartz.jobStore.useProperties
“使用属性”标志指示JDBCJobStore,JobDataMaps中的所有值都是字符串,因此可以作为名称/值对存储,而不是将更复杂的对象以其序列化形式存储在BLOB列中。这很方便,因为避免了将非String类序列化为BLOB可能引起的类版本控制问题。
org.quartz.jobStore.misfireThreshold
调度程序在被认为是“误触发”之前,将“容忍”触发器经过下一次触发时间的毫秒数。默认值(如果未在配置中输入此属性)为60000(60秒)。
org.quartz.jobStore.isClustered
设置为“ true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则必须将此属性设置为“ true”。否则,您将遭受严重破坏。有关更多信息,请参见配置文档。
org.quartz.jobStore.clusterCheckinInterval
设置此实例与其他群集实例“签入” *的频率(以毫秒为单位)。影响检测失败实例的速度。
org.quartz.jobStore.maxMisfiresToHandleAtATime
作业存储将在给定通道中处理的未触发触发器的最大数量。一次处理许多(多于几十打)会导致数据库表被锁定足够长的时间,从而可能会触发其他(尚未触发)的触发器。
org.quartz.jobStore.dontSetAutoCommitFalse
将此参数设置为“ true”将告诉Quartz不要在从DataSource获得的连接上调用setAutoCommit(false)。在某些情况下,这可能会很有帮助,例如,如果您的司机在已经关闭时抱怨它是否被调用。此属性默认为false,因为大多数驱动程序要求调用setAutoCommit(false)。
org.quartz.jobStore.selectWithLockSQL
必须是在“ LOCKS”表中选择一行并在该行上放置锁的SQL字符串。如果未设置,则默认值为“ SELECT * FROM {0} SCHED_NAME = {1}且LOCK_NAME =?FOR UPDATE”,适用于大多数数据库。在运行时,“ {0}”将替换为上面配置的TABLE_PREFIX。“ {1}”将替换为调度程序的名称。
org.quartz.jobStore.txIsolationLevelSerializable
值“ true”告诉Quartz(使用JobStoreTX或CMT时)在JDBC连接上调用setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE)。这有助于防止某些数据库在高负载下锁定超时以及“持久”事务。
org.quartz.jobStore.acquireTriggersWithinLock
是否获取下一个触发触发器应在显式数据库锁内进行。曾经有必要(在Quartz的早期版本中)避免特定数据库的死锁,但是不再认为这是必需的,因此默认值为“ false”。
如果将“ org.quartz.scheduler.batchTriggerAcquisitionMaxCount”设置为> 1,并且使用JDBC JobStore,则必须将此属性设置为“ true”,以避免数据损坏(从Quartz 2.1.1开始,“ true”为默认值)如果batchTriggerAcquisitionMaxCount设置为> 1)。
org.quartz.jobStore.lockHandler.class
用于生成org.quartz.impl.jdbcjobstore.Semaphore实例的类名称,该实例用于锁定对作业存储数据的控制。这是高级配置功能,大多数用户不应使用。默认情况下,Quartz将选择要使用的最合适的(预捆绑)信号量实现。MS SQL Server用户可能会对“ org.quartz.impl.jdbcjobstore.UpdateLockRowSemaphore” QUARTZ-497感兴趣。参见QUARTZ-441。
org.quartz.jobStore.driverDelegateInitString
管道分隔的属性(及其值)列表,可在初始化期间传递给DriverDelegate。
字符串的格式如下:
“ settingName =设置值| otherSettingName = otherSettingValue | ...”
StdJDBCDelegate及其所有后代(与Quartz一起提供的所有委托)都支持一个名为“ triggerPersistenceDelegateClasses”的属性,该属性可以设置为以逗号分隔的类列表,这些类实现了TriggerPersistenceDelegate接口,用于存储自定义触发器类型。有关为自定义触发器编写持久性委托的示例,请参见Java类SimplePropertiesTriggerPersistenceDelegateSupport和SimplePropertiesTriggerPersistenceDelegateSupport。
配置数据源
如果使用的是JDBC-Jobstore,则需要一个数据源供其使用(如果使用JobStoreCMT,则需要两个数据源)。
可以通过三种方式配置数据源:
- 在quartz.properties文件中指定了所有池属性,以便Quartz可以创建数据源本身。
- 可以指定应用服务器管理的数据源的JNDI位置,以便Quartz可以使用它。
- 自定义定义的org.quartz.utils.ConnectionProvider实现。
建议将您的数据源最大连接大小配置为至少为线程池中的工作线程数加三个。如果您的应用程序还频繁调用Scheduler API,则可能需要其他连接。如果使用JobStoreCMT,则“非托管”数据源的最大连接大小应至少为四个。
您定义的每个数据源(通常为一个或两个)必须被赋予一个名称,并且您为每个数据源定义的属性必须包含该名称,如下所示。数据源的“名称”可以是您想要的任何东西,除了在将其分配给JDBCJobStore时能够识别它之外,没有其他意义。
Quartz创建的数据源使用以下属性定义:
物业名称 | 需要 | 类型 | 默认值 |
---|---|---|---|
org.quartz.dataSource.NAME.driver | 是 | 串 | 空值 |
org.quartz.dataSource.NAME.URL | 是 | 串 | 空值 |
org.quartz.dataSource.NAME.user | 没有 | 串 | ” |
org.quartz.dataSource.NAME.password | 没有 | 串 | ” |
org.quartz.dataSource.NAME.maxConnections | 没有 | 整型 | 10 |
org.quartz.dataSource.NAME.validationQuery | 没有 | 串 | 空值 |
org.quartz.dataSource.NAME.idleConnectionValidationSeconds | 没有 | 整型 | 50 |
org.quartz.dataSource.NAME.validateOnCheckout | 没有 | 布尔值 | 假 |
org.quartz.dataSource.NAME.discardIdleConnectionsSeconds | 没有 | 整型 | 0(禁用) |
org.quartz.dataSource.NAME.driver
必须是数据库的JDBC驱动程序的Java类名称。
org.quartz.dataSource.NAME.URL
用于连接数据库的连接URL(主机,端口等)。
org.quartz.dataSource.NAME.user
连接到数据库时要使用的用户名。
org.quartz.dataSource.NAME.password
连接到数据库时使用的密码。
org.quartz.dataSource.NAME.maxConnections
DataSource可以在其连接池中创建的最大连接数。
org.quartz.dataSource.NAME.validationQuery
是一个可选的SQL查询字符串,数据源可用于检测和替换失败/损坏的连接。
例如,一个oracle用户可能会选择“从user_tables中选择table_name”-这是一个永远不会失败的查询-除非实际上连接不好。
org.quartz.dataSource.NAME.idleConnectionValidationSeconds
空闲连接测试之间的秒数-仅在设置了验证查询属性后才启用。
默认值为50秒。
org.quartz.dataSource.NAME.validateOnCheckout
每次从池中检索连接以确保其仍然有效时,是否应该执行数据库sql查询以验证连接。如果为假,则将在入住时进行验证。默认为false。
org.quartz.dataSource.NAME.discardIdleConnectionsSeconds
闲置许多秒后,丢弃连接。0禁用该功能。默认值为0。
Quartz定义的数据源的示例
org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@ 10.0.1.23:1521:demodb
org.quartz.dataSource.myDS.user = myUser
org.quartz.dataSource.myDS.password = myPassword
org.quartz.dataSource.myDS.maxConnections = 30
使用以下属性定义对Application Server数据源的引用:
物业名称 | 需要 | 类型 | 默认值 |
---|---|---|---|
org.quartz.dataSource.NAME.jndiURL | 是 | 串 | 空值 |
org.quartz.dataSource.NAME.java.naming.factory.initial | 没有 | 串 | 空值 |
org.quartz.dataSource.NAME.java.naming.provider.url | 没有 | 串 | 空值 |
org.quartz.dataSource.NAME.java.naming.security.principal | 没有 | 串 | 空值 |
org.quartz.dataSource.NAME.java.naming.security.credentials | 没有 | 串 | 空值 |
org.quartz.dataSource.NAME.jndiURL
由您的应用程序服务器管理的数据源的JNDI URL。
org.quartz.dataSource.NAME.java.naming.factory.initial
您希望使用的JNDI InitialContextFactory的(可选)类名称。
org.quartz.dataSource.NAME.java.naming.provider.url
用于连接到JNDI上下文的(可选)URL。
org.quartz.dataSource.NAME.java.naming.security.principal
用于连接到JNDI上下文的(可选)用户主体。
org.quartz.dataSource.NAME.java.naming.security.credentials
用于连接到JNDI上下文的(可选)用户凭证。
从应用程序服务器引用的数据源的示例
org.quartz.dataSource.myOtherDS.jndiURL = jdbc / myDataSource
org.quartz.dataSource.myOtherDS.java.naming.factory.initial = com.evermind.server.rmi.RMIInitialContextFactory
org.quartz.dataSource.myOtherDS.java.naming.provider.url = ormi://本地主机
org.quartz.dataSource.myOtherDS.java.naming.security.principal = admin
org.quartz.dataSource.myOtherDS.java.naming.security.credentials = 123
自定义ConnectionProvider实现
物业名称 | 需要 | 类型 | 默认值 |
---|---|---|---|
org.quartz.dataSource.NAME.connectionProvider.class | 是 | 字符串(类名) | 空值 |
org.quartz.dataSource.NAME.connectionProvider.class
要使用的ConnectionProvider的类名。实例化该类后,Quartz可以在实例上以bean样式自动设置配置属性。
使用自定义ConnectionProvider实现的示例
org.quartz.dataSource.myCustomDS.connectionProvider.class = com.foo.FooConnectionProvider
org.quartz.dataSource.myCustomDS.someStringProperty = someValue
org.quartz.dataSource.myCustomDS.someIntProperty = 5
环境前缀:
@Value("${spring.profiles.active}")
private String env;
Job
HarInfoEhcacheTask
1
cronExpression
0 */15 6-9 * MON ?
misfirePolicy
1 //忽略所有发生misfire的任务,然后按照原计划继续执行
try {
String result = redisUtil.hget(key, contextMd5);
if (StringUtils.isNotBlank(result)) {
return gson.fromJson(result, SqlQueryResultDto.class);
}
} catch (Exception e) {
log.error("get sql result cache failed:key:{},e:{}", key, e.getMessage());
}
private Map<String,WeekDeptDataVO> getMapWeekDeptDateVO(String pt) {
//部门详细信息类
String deptPt = pt;
if(!StringUtils.isNotBlank(deptPt)) {
deptPt = sqlQueryService.getDatasourceNewestPt(WeekTableSource.WEEK_TABLE_SOURCE.getDeptSource());
}
String querySql = "select * from " + WeekTableSource.WEEK_TABLE_SOURCE.getDeptSource() +
" where pt=\'" + deptPt + "\'";
try {
List<LinkedHashMap<String, Object>> weekDeptDetails = prestoSqlMapper.customQuery(querySql);
List<WeekDeptDataVO> listWeekDeptDataVOs = weekDeptDetails.stream().map(weekDeptDetailsMap->{
T3ManageInfoVO t3ManageInfoVO = new T3ManageInfoVO();
WeekDeptDataVO weekDeptDataVO = new WeekDeptDataVO();
t3ManageInfoVO.setDepartment1((String)weekDeptDetailsMap.get("dept_desc1"));
t3ManageInfoVO.setDeptId1((String)weekDeptDetailsMap.get("dept_id1"));
t3ManageInfoVO.setDepartment2((String)weekDeptDetailsMap.get("dept_desc2"));
t3ManageInfoVO.setDeptId2((String)weekDeptDetailsMap.get("dept_id2"));
t3ManageInfoVO.setDeptId3((String)weekDeptDetailsMap.get("dept_id3"));
t3ManageInfoVO.setDepartment3((String)weekDeptDetailsMap.get("dept_desc3"));
weekDeptDataVO.setT3Manage(t3ManageInfoVO);
weekDeptDataVO.setHealthRank((Integer)weekDeptDetailsMap.get("score_rank"));
weekDeptDataVO.setHealthScore((Integer)weekDeptDetailsMap.get("score"));
weekDeptDataVO.setImpactFactorRank((Integer)weekDeptDetailsMap.get("impact_factor_rank"));
weekDeptDataVO.setStorage(Objects.toString(weekDeptDetailsMap.get("sum_storage")));
weekDeptDataVO.setStorageRank((Integer)weekDeptDetailsMap.get("storage_rank"));
weekDeptDataVO.setTotal((Integer)weekDeptDetailsMap.get("t1_t3team_num"));
return weekDeptDataVO;
}).collect(Collectors.toList());
Map<String,WeekDeptDataVO> mapWeekDeptDataVO = listWeekDeptDataVOs.stream().collect(Collectors.toMap(k -> { return k.getT3Manage().getDeptId3();}, weekDeptDataVO -> weekDeptDataVO,(oldWeekDeptDataVO, newWeekDeptDataVO) -> oldWeekDeptDataVO));
return mapWeekDeptDataVO;
} catch (Exception e) {
throw new BusinessException(BusinessCode.ERROR_GET_DATA_FAILED, e.getMessage());
}
}