Java并发协作控制

线程协作

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
输入要查询的属性学生姓名:

定时任务

  1. 简单定时器机制
    设置计划任务,也就是在指定的时间开始执行某一个任务
描述
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());
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值