线程协作
1. Thread/Executor/Fork-Join
※ 线程启动,运行,结束
※ 线程之间缺少写作
2. synchronized同步
※ 限定只有一个线程才能进入关键区
※ 简单粗暴,性能损失有点大
3. Lock
Lock也可以实现同步的效果
※ 实现更复杂的临界区结构
※ tryLock方法可以预测锁是否空闲
※ 允许分离读写的操作,多个读,一个写
※ 性能更好
ReentrantLock类:可重入的互斥锁
ReentranReadWriteLock类:可重入的读写锁
这两个类中主要的函数是:lock和unlock函数
例子:
有家奶茶店,点单有时需要排队
假设想买奶茶的人如果看到需要排队,就决定不买
又假设奶茶店有老板和多名员工,记单方式比较原始,只有一个订单本
老板负责写新订单,员工不断地查看订单本得到信息来制作奶茶,在老板写新订单时员工不能看订单本
多个员工可同时看订单本,在员工看时老板不能写新订单
自定义函数 | 作用 |
---|---|
buyMilkTea | 调用tryToBuyMilkTea函数买奶茶 |
handleOrder | 调用老板添加订单,员工查看订单本的函数 |
addOrder | 老板添加订单的函数 |
viewOrder | 员工查看订单的函数 |
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class LockExample {
//两个Lock的类
private static final ReentrantLock queueLock = new ReentrantLock(); //可重入锁
private static final ReentrantReadWriteLock orderLock = new ReentrantReadWriteLock(); //可重入读写锁
/**
* 有家奶茶店,点单有时需要排队
* 假设想买奶茶的人如果看到需要排队,就决定不买
* 又假设奶茶店有老板和多名员工,记单方式比较原始,只有一个订单本
* 老板负责写新订单,员工不断地查看订单本得到信息来制作奶茶,在老板写新订单时员工不能看订单本
* 多个员工可同时看订单本,在员工看时老板不能写新订单
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
//buyMilkTea();//买奶茶的函数,调用tryToBuyMilkTea函数
handleOrder(); //需手动关闭,老板负责写员工负责读
}
//尝试买奶茶的函数
public void tryToBuyMilkTea() throws InterruptedException {
boolean flag = true;
while(flag)
{
if (queueLock.tryLock()) { //尝试加锁,其中queueLock是一个可重入的互斥锁ReentrantLock
//tryLock包含了queueLock.lock();
long thinkingTime = (long) (Math.random() * 500);
Thread.sleep(thinkingTime);//休眠一段时间
System.out.println(Thread.currentThread().getName() + ": 来一杯珍珠奶茶,不要珍珠");
flag = false;
queueLock.unlock(); //释放资源
} else {//加锁不成功,输出:线程名+"再等等"
//System.out.println(Thread.currentThread().getName() + ":" + queueLock.getQueueLength() + "人在排队");
System.out.println(Thread.currentThread().getName() + ": 再等等");
}
if(flag)//如果flag=true,那就等一秒钟,再进入if-else
{
Thread.sleep(1000);
}
}
}
//老板添加订单,读写锁
public void addOrder() throws InterruptedException {
orderLock.writeLock().lock();//加锁,之后添加记录
long writingTime = (long) (Math.random() * 1000);
Thread.sleep(writingTime);
System.out.println("老板新加一笔订单");
orderLock.writeLock().unlock();//释放锁
}
//查看订单本,读锁,可以多个线程共享
public void viewOrder() throws InterruptedException {
orderLock.readLock().lock();
long readingTime = (long) (Math.random() * 500);
Thread.sleep(readingTime);
System.out.println(Thread.currentThread().getName() + ": 查看订单本");
orderLock.readLock().unlock();
}
//1. 买奶茶的函数
public static void buyMilkTea() throws InterruptedException {
//① 首先产生实例
LockExample lockExample = new LockExample();
//有10个学生
int STUDENTS_CNT = 10;
//新建一个线程数组students[];
Thread[] students = new Thread[STUDENTS_CNT];
for (int i = 0; i < STUDENTS_CNT; i++) {
//把线程数组初始化,并让所有的线程启动
students[i] = new Thread(new Runnable() {
@Override
public void run() {//匿名线程类,可以让所有的线程启动
try {
long walkingTime = (long) (Math.random() * 1000);
Thread.sleep(walkingTime);
lockExample.tryToBuyMilkTea();//调用尝试买奶茶的函数tryToBuyMilkTea()
} catch(InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
);
students[i].start();
}
for (int i = 0; i < STUDENTS_CNT; i++)
students[i].join();
}
//操作订单的函数,调用老板添加订单,员工查看订单
public static void handleOrder() throws InterruptedException {
//创建一个线程
LockExample lockExample = new LockExample();
//初始化线程并运行
Thread boss = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
lockExample.addOrder();//调用老板添加订单的函数
long waitingTime = (long) (Math.random() * 1000);
Thread.sleep(waitingTime);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
});
boss.start();
//三个员工
int workerCnt = 3;
Thread[] workers = new Thread[workerCnt];
for (int i = 0; i < workerCnt; i++)
{
//实现了员工的匿名对象类
workers[i] = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
lockExample.viewOrder();//员工查看订单本的函数
long workingTime = (long) (Math.random() * 5000);
Thread.sleep(workingTime);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
}
});
workers[i].start();
}
}
}
买奶茶的运行结果
Thread-7: 来一杯珍珠奶茶,不要珍珠
Thread-8: 再等等
Thread-6: 再等等
Thread-5: 来一杯珍珠奶茶,不要珍珠
Thread-1: 再等等
Thread-0: 再等等
Thread-2: 再等等
Thread-3: 再等等
Thread-9: 再等等
Thread-4: 来一杯珍珠奶茶,不要珍珠
Thread-6: 再等等
Thread-1: 再等等
Thread-0: 再等等
Thread-8: 来一杯珍珠奶茶,不要珍珠
Thread-3: 再等等
Thread-9: 再等等
Thread-2: 来一杯珍珠奶茶,不要珍珠
Thread-1: 再等等
Thread-0: 再等等
Thread-3: 再等等
Thread-6: 来一杯珍珠奶茶,不要珍珠
Thread-9: 来一杯珍珠奶茶,不要珍珠
Thread-0: 再等等
Thread-3: 再等等
Thread-1: 来一杯珍珠奶茶,不要珍珠
Thread-3: 再等等
Thread-0: 来一杯珍珠奶茶,不要珍珠
Thread-3: 来一杯珍珠奶茶,不要珍珠
订单操作的运行结果
老板新加一笔订单
Thread-2: 查看订单本
Thread-3: 查看订单本
Thread-1: 查看订单本
老板新加一笔订单
老板新加一笔订单
老板新加一笔订单
老板新加一笔订单
Thread-1: 查看订单本
老板新加一笔订单
Thread-2: 查看订单本
Thread-3: 查看订单本
老板新加一笔订单
Thread-3: 查看订单本
老板新加一笔订单
Thread-1: 查看订单本
老板新加一笔订单
老板新加一笔订单
老板新加一笔订单
Thread-2: 查看订单本
老板新加一笔订单
Thread-3: 查看订单本
老板新加一笔订单
Thread-1: 查看订单本
Thread-2: 查看订单本
老板新加一笔订单
Thread-3: 查看订单本
Thread-1: 查看订单本
老板新加一笔订单
Thread-2: 查看订单本
老板新加一笔订单
4. Semaphore
※ 信号量,1965年由Dijkstra提出,本质上是一个计数器
※ 1. 计数器大于0,可以使用,等于0不能使用
※ 2. 可以设置多个并发量,例如限制10个访问
※ 3. 可以设置多个并发量,例如限制10个访问
※ Semaphore的方法:
方法名 | 作用 |
---|---|
acquire | 获取一个信号量,即把信号量减1 |
release | 释放一个信号量,即把信号量加1 |
※ 比Lock更进一步,可以控制多个同时访问关键区
例子:
地下车库停车,一共5个车位,10辆车
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private final Semaphore placeSemaphore = new Semaphore(5);//限定限号量为5个
public boolean parking() throws InterruptedException {
if (placeSemaphore.tryAcquire()) {//acquire,使信号量-1,信号量能被获取到,则停车成功
System.out.println(Thread.currentThread().getName() + ": 停车成功");
return true;
} else {//信号量没申请到,则停车失败
System.out.println(Thread.currentThread().getName() + ": 没有空位");
return false;
}
}
public void leaving() throws InterruptedException {
placeSemaphore.release();//release,使信号量+1
System.out.println(Thread.currentThread().getName() + ": 开走");
}
/**
* 现有一地下车库,共有车位5个,由10辆车需要停放,每次停放时,去申请信号量
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
int tryToParkCnt = 10;
SemaphoreExample semaphoreExample = new SemaphoreExample();
//需要停车的人的数组
Thread[] parkers = new Thread[tryToParkCnt];
for (int i = 0; i < tryToParkCnt; i++) {
//对每一个停车者,都实现了一个匿名的线程对象类
parkers[i] = new Thread(new Runnable() {
@Override
public void run() {
try {
long randomTime = (long) (Math.random() * 1000);
Thread.sleep(randomTime);
if (semaphoreExample.parking()) {
long parkingTime = (long) (Math.random() * 1200);
Thread.sleep(parkingTime);
semaphoreExample.leaving();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
parkers[i].start();
}
for (int i = 0; i < tryToParkCnt; i++) {
parkers[i].join();
}
}
}
运行结果
Thread-7: 停车成功
Thread-5: 停车成功
Thread-3: 停车成功
Thread-3: 开走
Thread-8: 停车成功
Thread-0: 停车成功
Thread-4: 停车成功
Thread-4: 开走
Thread-1: 停车成功
Thread-1: 开走
Thread-2: 停车成功
Thread-2: 开走
Thread-6: 停车成功
Thread-9: 没有空位
Thread-5: 开走
Thread-7: 开走
Thread-0: 开走
Thread-8: 开走
Thread-6: 开走
5. Latch
※ 等待锁,是一个同步辅助类,保证所有线程都到了(latch变成0),然后大家一起走
※ 用来同步执行任务的一个或者多个线程
※ 不是用来保护临界区或者共享资源
※ CountDownLatch类的两个方法
方法名 | 作用 |
---|---|
countDownLatch() | 计数减1,把等待的await()变成0 |
await() | 等待latch变成0,变成0以后,await() 就会解锁 |
例子
10个选手跑步比赛,发令枪响为开始,10个选手到终点为比赛结束
自定义函数 | 作用 |
---|---|
main | 调用countDown,让发令枪线程减startSignal1,调用await,等待doneSignal由10减成0 |
Worker | 等待startSignal变成0,调用countDown方法,让doneSignal变成0 |
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
/**
* 设想百米赛跑比赛 发令枪发出信号后选手开始跑,全部选手跑到终点后比赛结束
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
int runnerCnt = 10;
CountDownLatch startSignal = new CountDownLatch(1);//发令枪,计数为1
CountDownLatch doneSignal = new CountDownLatch(runnerCnt);//比赛结束,计数为10
for (int i = 0; i < runnerCnt; ++i) // 创建并启动线程
new Thread(new Worker(startSignal, doneSignal)).start();
System.out.println("准备工作...");
System.out.println("准备工作就绪");
startSignal.countDown(); // 调用countDown方法,让startSignal减1,变成0
System.out.println("比赛开始");
doneSignal.await(); // 等待doneSignal变成0,doneSignal本来是10
System.out.println("比赛结束");
}
static class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await();//startSignal是一个Latch,调用await,等待startSignal变成0
doWork();
doneSignal.countDown();//每个人跑到终点,doneSignal就减1,总共10
} catch (InterruptedException ex) {
} // return;
}
void doWork() {
System.out.println(Thread.currentThread().getName() + ": 跑完全程");
}
}
}
运行结果
准备工作...
准备工作就绪
比赛开始
Thread-0: 跑完全程
Thread-5: 跑完全程
Thread-2: 跑完全程
Thread-3: 跑完全程
Thread-7: 跑完全程
Thread-4: 跑完全程
Thread-1: 跑完全程
Thread-8: 跑完全程
Thread-6: 跑完全程
Thread-9: 跑完全程
比赛结束
5. Barrier
※ 集合点,也是一个同步辅助类
※ 允许多个线程在某一个点上进行同步
※ CyclicBarrier
△ 构造函数式需要同步的线程数量
△ await等待其他线程,达到数量后,就放行,
△ 适合用于归并的程序,每个子线程都去算自己应该做的事情,等到所有的子线程都做完了以后,合并的那个线程就可以启动了
案例
计算一个二维数组中所有数的和,先计算每行的和,再计算总共的和
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
/**
* 假定有三行数,用三个线程分别计算每一行的和,最终计算总和
* @param args
*/
public static void main(String[] args) {
final int[][] numbers = new int[3][5];
final int[] results = new int[3];
int[] row1 = new int[]{1, 2, 3, 4, 5};
int[] row2 = new int[]{6, 7, 8, 9, 10};
int[] row3 = new int[]{11, 12, 13, 14, 15};
numbers[0] = row1;
numbers[1] = row2;
numbers[2] = row3;
CalculateFinalResult finalResultCalculator = new CalculateFinalResult(results);//CalculateFinalResult是一个计算结果的线程,后面有定义
CyclicBarrier barrier = new CyclicBarrier(3, finalResultCalculator);//CyclicBarrier集合点,里面有3个线程
//当有3个线程在barrier上await,就执行finalResultCalculator
for(int i = 0; i < 3; i++) {
CalculateEachRow rowCalculator = new CalculateEachRow(barrier, numbers, i, results);
new Thread(rowCalculator).start();
}
}
}
class CalculateEachRow implements Runnable {
final int[][] numbers;
final int rowNumber;
final int[] res;
final CyclicBarrier barrier;
CalculateEachRow(CyclicBarrier barrier, int[][] numbers, int rowNumber, int[] res) {
this.barrier = barrier;
this.numbers = numbers;
this.rowNumber = rowNumber;
this.res = res;
}
@Override
public void run() {
int[] row = numbers[rowNumber];
int sum = 0;
for (int data : row) {
sum += data;
res[rowNumber] = sum;
}
try {
System.out.println(Thread.currentThread().getName() + ": 计算第" + (rowNumber + 1) + "行结束,结果为: " + sum);
barrier.await(); //等待!只要超过3个(Barrier的构造参数),就放行。
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
class CalculateFinalResult implements Runnable {
final int[] eachRowRes;
int finalRes;
public int getFinalResult() {
return finalRes;
}
CalculateFinalResult(int[] eachRowRes) {
this.eachRowRes = eachRowRes;
}
@Override
public void run() {
int sum = 0;
for(int data : eachRowRes) {
sum += data;
}
finalRes = sum;
System.out.println("最终结果为: " + finalRes);
}
}
运行结果
Thread-0: 计算第1行结束,结果为: 15
Thread-1: 计算第2行结束,结果为: 40
Thread-2: 计算第3行结束,结果为: 65
最终结果为: 120
Phaser
※ 允许执行并发多阶段任务,同步辅助类
※ 在每一个阶段结束的位置对线程进行同步,当所有的线程都打到这步,再进行下一步
※ Phaser,可以多次来应用,而Barrier只能应用一次
方法 | 作用 |
---|---|
arrive() | |
arriveAndAwaitAdvance() | 等待 |
案例
import java.util.concurrent.Phaser;
public class PhaserExample {
/**
* 假设举行考试,总共三道大题,每次下发一道题目,等所有学生(五个学生)完成后再进行下一道
*
* @param args
*/
public static void main(String[] args) {
int studentsCnt = 5;
Phaser phaser = new Phaser(studentsCnt);//5个线程要等待
for (int i = 0; i < studentsCnt; i++) {
new Thread(new Student(phaser)).start();
}
}
}
class Student implements Runnable {
private final Phaser phaser;
public Student(Phaser phaser) {
this.phaser = phaser;
}
@Override
public void run() {
try {
doTesting(1);
phaser.arriveAndAwaitAdvance(); //等到5个线程都到了,才放行到第二题
doTesting(2);
phaser.arriveAndAwaitAdvance(); //等到5个线程都到了,才放行到第三题
doTesting(3);
phaser.arriveAndAwaitAdvance(); //等到5个线程都到了,才放行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void doTesting(int i) throws InterruptedException {
String name = Thread.currentThread().getName();
System.out.println(name + "开始答第" + i + "题");
long thinkingTime = (long) (Math.random() * 1000);
Thread.sleep(thinkingTime);
System.out.println(name + "第" + i + "道题答题结束");
}
}
运行结果
Thread-1开始答第1题
Thread-0开始答第1题
Thread-4开始答第1题
Thread-3开始答第1题
Thread-2开始答第1题
Thread-3第1道题答题结束
Thread-0第1道题答题结束
Thread-1第1道题答题结束
Thread-2第1道题答题结束
Thread-4第1道题答题结束
Thread-1开始答第2题
Thread-0开始答第2题
Thread-3开始答第2题
Thread-2开始答第2题
Thread-4开始答第2题
Thread-0第2道题答题结束
Thread-2第2道题答题结束
Thread-3第2道题答题结束
Thread-4第2道题答题结束
Thread-1第2道题答题结束
Thread-4开始答第3题
Thread-3开始答第3题
Thread-0开始答第3题
Thread-2开始答第3题
Thread-1开始答第3题
Thread-2第3道题答题结束
Thread-3第3道题答题结束
Thread-4第3道题答题结束
Thread-1第3道题答题结束
Thread-0第3道题答题结束
Exchanger
※ 允许在并发线程中互相交换消息
※ 允许在2个线程中定义同步点,当两个线程都打到同步点,它们交换数据结构
※ Exchanger
方法 | 作用 |
---|---|
exchange() | 线程双方互相交互数据 |
- | 交换数据是双向的 |
案例
成绩查询,输入姓名与后台交换数据
import java.util.Scanner;
import java.util.concurrent.Exchanger;
public class ExchangerExample {
/**
* 本例通过Exchanger实现学生成绩查询,简单线程间数据的交换
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Exchanger<String> exchanger = new Exchanger<String>();//定义一个Exchanger,泛型String
BackgroundWorker worker = new BackgroundWorker(exchanger);
new Thread(worker).start(); //启动
//Scanner接收外界输入
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.println("输入要查询的属性学生姓名:");
String input = scanner.nextLine().trim();
exchanger.exchange(input);
/**把用户输入传递给线程,此处的exchange与44行对应"String item = exchanger.exchange(null)"进行数据交换
* 当两个线程都同时执行到同一个exchanger的exchange方法
* 两个线程就互相交换数据,交换是双向的
*/
String value = exchanger.exchange(null); //拿到线程反馈结果,与线程run方法里的exchanger交换数据
if ("exit".equals(value)) {
break;
}
System.out.println("查询结果:" + value);
}
scanner.close();
}
}
class BackgroundWorker implements Runnable {
final Exchanger<String> exchanger;
BackgroundWorker(Exchanger<String> exchanger) {
this.exchanger = exchanger;
}
@Override
public void run() {
while (true) {
try {
String item = exchanger.exchange(null);
switch (item) {
case "zhangsan":
exchanger.exchange("90");
break;
case "lisi":
exchanger.exchange("80");
break;
case "wangwu":
exchanger.exchange("70");
break;
case "exit":
exchanger.exchange("exit");
return;
default:
exchanger.exchange("查无此人");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
控制台输入输出
输入要查询的属性学生姓名:
zhangsan
查询结果:90
输入要查询的属性学生姓名:
查询结果:查无此人
输入要查询的属性学生姓名:
lisi
查询结果:80
输入要查询的属性学生姓名:
wangwu
查询结果:70
输入要查询的属性学生姓名:
定时任务
- 简单定时器机制
设置计划任务,也就是在指定的时间开始执行某一个任务
类 | 描述 |
---|---|
TimerTask | 封装任务 |
Timer | 定时器 |
案例
package timer;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest {
public static void main(String[] args) throws InterruptedException {
MyTask task = new MyTask();
Timer timer = new Timer();
System.out.println("当前时间:"+new Date().toLocaleString());
//schedule是调度,当前时间1秒后,每2秒执行一次
timer.schedule(task, 1000, 2000);
Thread.sleep(10000);//睡眠1秒钟
task.cancel(); //取消当前的任务
System.out.println("================================");
Calendar now = Calendar.getInstance();
now.set(Calendar.SECOND,now.get(Calendar.SECOND)+3);
Date runDate = now.getTime();
MyTask2 task2 = new MyTask2();
timer.scheduleAtFixedRate(task2,runDate,3000); //固定速率
Thread.sleep(20000);
timer.cancel(); //取消定时器
}
}
class MyTask extends TimerTask {
public void run() {
System.out.println("运行了!时间为:" + new Date());
}
}
class MyTask2 extends TimerTask {
public void run() {
System.out.println("运行了!时间为:" + new Date());
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果
当前时间:2020-4-9 17:25:23
运行了!时间为:Thu Apr 09 17:25:24 CST 2020
运行了!时间为:Thu Apr 09 17:25:26 CST 2020
运行了!时间为:Thu Apr 09 17:25:28 CST 2020
运行了!时间为:Thu Apr 09 17:25:30 CST 2020
运行了!时间为:Thu Apr 09 17:25:32 CST 2020
================================
运行了!时间为:Thu Apr 09 17:25:36 CST 2020
运行了!时间为:Thu Apr 09 17:25:40 CST 2020
运行了!时间为:Thu Apr 09 17:25:44 CST 2020
运行了!时间为:Thu Apr 09 17:25:48 CST 2020
运行了!时间为:Thu Apr 09 17:25:52 CST 2020
并行执行定时器-Executor+定时器
※ 线程池:ScheduledExecutorService
executeAtFixTime()运行结果
时间为:Thu Apr 09 18:45:31 CST 2020
executeFixedRate运行结果
时间为:Thu Apr 09 18:45:57 CST 2020
时间为:Thu Apr 09 18:46:00 CST 2020
时间为:Thu Apr 09 18:46:03 CST 2020
时间为:Thu Apr 09 18:46:06 CST 2020
时间为:Thu Apr 09 18:46:09 CST 2020
时间为:Thu Apr 09 18:46:12 CST 2020
...
executeFixedDelay运行结果
时间为:Thu Apr 09 18:46:41 CST 2020
时间为:Thu Apr 09 18:46:45 CST 2020
时间为:Thu Apr 09 18:46:49 CST 2020
时间为:Thu Apr 09 18:46:53 CST 2020
时间为:Thu Apr 09 18:46:57 CST 2020
定时任务-Quartz
Quartz |
---|
Quartz是一个较为完善的任务调度框架 |
解决程序中Timer零散管理的问题 |
功能更强大 |
Timer执行周期任务,如果中间某一次有异常,整个任务终止执行 |
Quartz执行周期任务,如果中间某一次有异常,不影响下次任务执行 |
QuartzTest.java
package quartz;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
public class QuartzTest {
public static void main(String[] args) {
try {
//创建scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//定义一个Trigger
Trigger trigger = newTrigger().withIdentity("trigger1", "group1") //定义name/group
.startNow()//一旦加入scheduler,立即生效
.withSchedule(simpleSchedule() //使用SimpleTrigger
.withIntervalInSeconds(2) //每隔2秒执行一次
.repeatForever()) //可以控制执行几次后结束,在这里一直执行
.build();
//定义一个JobDetail
JobDetail job = newJob(HelloJob.class) //定义Job类为HelloQuartz类
.withIdentity("job1", "group1") //定义name/group
.usingJobData("name", "quartz") //定义属性
.build();
//加入这个调度
scheduler.scheduleJob(job, trigger);
//启动
scheduler.start();
//运行一段时间后关闭
Thread.sleep(10000);
scheduler.shutdown(true);
} catch (Exception e) {
e.printStackTrace();
}
}
}
HelloJob.java
package quartz;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
public class HelloJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDetail detail = context.getJobDetail();
String name = detail.getJobDataMap().getString("name");
System.out.println("hello from " + name + " at " + new Date());
}
}