1.任务定时调度
通过Timer和Timertask,可以实现定时启动某个线程。
- java.util.Timer:雷诗雨闹钟的功能,本身实现的就是一个线程
- java.util.TimerTask:一个抽象类,该类实现了Runnable接口,所以该类具备多线程的能力
- public void schedule(TimerTask task, long delay)
-
在指定的延迟之后安排指定的任务执行。
参数
task
- 要安排的任务。delay
- 执行任务之前以delay
为单位的延迟。异常
IllegalArgumentException
- 如果 delay为负数,或 delay + System.currentTimeMillis()为负数。IllegalStateException
- 如果任务已经被调度或取消,定时器被取消或定时器线程被终止。NullPointerException
- 如果task
为空
-
2.代码
package cn.zwl.other;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
/**
* 任务调度:Timer和TimerTask
*
* @author dell
*
*/
public class TimerTest1 {
public static void main(String[] args) {
Timer time=new Timer();
//执行安排
//time.schedule(new MyTask(), 1000);//执行一次
//time.schedule(new MyTask(), 1000,200);//每隔200毫秒执行一次
Calendar cal=new GregorianCalendar(2020,4,3,17,55);
time.schedule(new MyTask(),cal.getTime(),2000);//每隔200毫秒执行一次
}
}
//任务类是一个多线程
class MyTask extends TimerTask{
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i <10; i++) {
System.out.println("休息一会");
}
System.out.println("-----休息结束----");
}
}
3.任务调度框架QUARTZ
Scheduler ----- 调度器,控制所有的调度
Trigger --------触发条件,采用DSL模式
JobDetail ------需要处理的Job
Job---------------执行逻辑
DSL:Domain-specific language领域特定的语言,针对一个特定的领域,具有受限表达性的一种计算机程序语言,即:
领域专用语言、声明式编程:
- Method Chaining 方法链 Fluent Style流程风格,builder模式构造器
- Nested Functions 嵌套函数
- Lambda Expressions
- Functional Sequence
/*
* All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package cn.zwl.other;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job {
private static Logger _log = LoggerFactory.getLogger(HelloJob.class);
public HelloJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println("----start------");
System.out.println("Hello World! - " + new Date());
System.out.println("-----end------");
}
}
package cn.zwl.other;
import static org.quartz.DateBuilder.evenSecondDateAfterNow;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
/**
* quartz学习
*/
public class Quartz {
public void run() throws Exception {
Logger log = LoggerFactory.getLogger(Quartz.class);
log.info("------- Initializing ----------------------");
// 1.创建Schedule的工厂
SchedulerFactory sf = new StdSchedulerFactory();
//2.从工厂中获取调度器
Scheduler sched = sf.getScheduler();
log.info("------- Initialization Complete -----------");
// computer a time that is on the next round minute
log.info("------- Scheduling Job -------------------");
// define the job and tie it to our HelloJob class
//3.创建 JobDetail
JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();
// 4.触发条件的创建
//时间
Date runTime = evenSecondDateAfterNow();
Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();
// Tell quartz to schedule the job using our trigger
//5.注册任务和触发条件
sched.scheduleJob(job, trigger);
log.info(job.getKey() + " will run at: " + runTime);
// Start up the scheduler (nothing can actually run until the
// scheduler has been started)
//启动
sched.start();
log.info("------- Started Scheduler -----------------");
// wait long enough so that the scheduler as an opportunity to
// run the job!
log.info("------- Waiting 5 seconds... -------------");
try {
Thread.sleep(5L * 1000L);//5秒停止
// executing...
} catch (Exception e) {
//
}
// shut down the scheduler
log.info("------- Shutting Down ---------------------");
sched.shutdown(true);
log.info("------- Shutdown Complete -----------------");
}
public static void main(String[] args) throws Exception {
Quartz example = new Quartz();
example.run();
}
}
3.HappenBefore
解读:
执行代码 的顺序可能与编写代码不一致,即虚拟机优化代码顺序,则为指令重排
HappenBefroe即:编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段
数据依赖:
只要操作同一个变量都会存在数据依赖,数据与数据没有依赖可能会有指令重排
代码练习:
package cn.zwl.other;
/**
*
* 指令重排
* @author dell
*
*/
public class HappenBefore {
//变量1
private static int a=0;
//变量2
private static boolean flag=false;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
a=0;
flag=false;
//线程1更改数据
Thread t1=new Thread(()-> {
a=1;
flag=true;
});
//线程2读取数据
Thread t2=new Thread(()-> {
if(flag) {
a*=1;
}
if(a==0) {
System.out.println("happenbefore a->"+a);;
}
});
t1.start();
t2.start();
//线程插队
t1.join();
t2.join();
}
}
}
4.volatile
不能保证原子性(拿数据、操作数据、存储数据)
保证线程间变量的可见性,具体来说符合以下两个规则:
- 线程对变量修改后,要立刻写回到主内存
- 线程对变量读取的时候,要从主内存中读取,而不是缓存。
代码练习:
package cn.zwl.other;
/**
*
* volatile用于保证数据的同步,即可见性
* @author dell
*
*/
public class VolatileTest {
private volatile static int num=0;
public static void main(String[] args) {
new Thread(()-> {
while (num==0) {
;
}
}).start();
try {
Thread.sleep(100);
num=1;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
5.单例模式
解读:
对外只有一个对象
如何使用double-checking和volitale进行锁定?
代码:
package cn.zwl.other;
/**
*
* DCL单例模式:懒汉式套路
* 多线程环境下,对外存在一个对象
* 1.构造器私有化--》避免外部new构造器
* 2.提供私有的静态属性--》存储对象的地址
* 3.提供公共的静态方法--》获取属性
* @author dell
*
*/
public class DoubleCheckedLocking {
//2.提供私有的静态属性
//没有volatile其他线程可能访问一个没有初始化的对象
private static volatile DoubleCheckedLocking instance;//懒汉式套路
private DoubleCheckedLocking() {
//1. 构造器的私有化
}
public static DoubleCheckedLocking getInstance() {
//再次检测
if (null!=instance) {
return instance;
}
synchronized (DoubleCheckedLocking.class) {
if(null==instance) {
instance=new DoubleCheckedLocking();
//new一个对象时会做三件事情
//1.开辟空间//2.初始化对象信息//3、返回对象的地址引用
}
}
return instance;
}
public static DoubleCheckedLocking getInstance1(long time) {
if(null==instance) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
instance=new DoubleCheckedLocking();
//new一个对象时会做三件事情
//1.开辟空间//2.初始化对象信息//3、返回对象的地址引用
}
return instance;
}
public static void main(String[] args) {
// //比较地址是否一致
// Thread t=new Thread(()-> {
// System.out.println(DoubleCheckedLocking.getInstance());
// });
// t.start();
// System.out.println(DoubleCheckedLocking.getInstance());
//测试不一致
Thread t1=new Thread(()-> {
System.out.println(DoubleCheckedLocking.getInstance1(500));
});
t1.start();
System.out.println(DoubleCheckedLocking.getInstance1(1000));
}
}
6.ThreadLocal
解读:ThreadLocal能够放一个线程级别的变量,其本身能够被多个线程共享使用,并且又能达到线程安全的目的。
JDK建议ThreadLocal定义为private static
常用方法:get/set/initalValue
常用于数据库的连接,HTTP请求,用户身份信息等。
代码:
package cn.zwl.other;
/**
*
* ThreadLocal学习
* get/set/initalValue
* @author dell
*
*/
public class ThreadLocalTest1 {
//private static ThreadLocal<Integer> threadLocal=new ThreadLocal<Integer>();
更改初始值
// private static ThreadLocal<Integer> threadLocal=new ThreadLocal<Integer>() {
// protected Integer initalValue() {
// return 200;
// }
//
// };
//更改2
private static ThreadLocal<Integer> threadLocal=ThreadLocal.withInitial(()->200);
//获取值
//主线程
public static void main(String[] args) {
threadLocal.set(99);
System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
//线程1
new Thread(new MyRun()).start();
//线程2
new Thread(new MyRun()).start();
}
static class MyRun implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
threadLocal.set((int)(Math.random()*99));
System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
}
}
}
package cn.zwl.other;
/**
*
* ThreadLocal学习
* get/set/initalValue
* InheritableThreadLocal:继承上下文 环境 起点
* 1.构造器:哪里调用就属于哪里 找线程体
* 2.run方法:本线程自身的
* @author dell
*
*/
public class ThreadLocalTest2 {
private static ThreadLocal<Integer> threadLocal=new InheritableThreadLocal<Integer>();
//获取值
//主线程
public static void main(String[] args) {
threadLocal.set(-100);
System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
//在start之前都是main线程的
//线程由main线程开辟
new Thread(()-> {
System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
}).start();
new Thread(new MyRun()).start();
}
public static class MyRun implements Runnable{
//注意两个Thread.currentThread().getName()是不一样的
public MyRun() {
// TODO Auto-generated constructor stub
System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
}
@Override
public void run() {
Integer left=threadLocal.get();
System.out.println(Thread.currentThread().getName()+"得到了-->"+left);
}
}
}
package cn.zwl.other;
/**
*
* ThreadLocal学习
* get/set/initalValue
* @author dell
*
*/
public class ThreadLocalTest3 {
private static ThreadLocal<Integer> threadLocal=ThreadLocal.withInitial(()->1);
//获取值
//主线程
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new MyRun()).start();
}
}
public static class MyRun implements Runnable{
@Override
public void run() {
Integer left=threadLocal.get();
System.out.println(Thread.currentThread().getName()+"得到了-->"+left);
threadLocal.set(left-1);
System.out.println(Thread.currentThread().getName()+"还剩下-->"+threadLocal.get());
}
}
}
package cn.zwl.other;
/**
*
* ThreadLocal学习
* get/set/initalValue
* @author dell
*
*/
public class ThreadLocalTest4 {
private static ThreadLocal<Integer> threadLocal=ThreadLocal.withInitial(()->1);
//获取值
//主线程
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new MyRun()).start();
}
}
public static class MyRun implements Runnable{
@Override
public void run() {
Integer left=threadLocal.get();
System.out.println(Thread.currentThread().getName()+"得到了-->"+left);
threadLocal.set(left-1);
System.out.println(Thread.currentThread().getName()+"还剩下-->"+threadLocal.get());
}
}
}