java----并发编程
任务定时调度
简单任务调度(Timer类)
public static void main(String[] args) { Timer timer = new Timer(); //1秒后执行run(),只执行一次 // timer.schedule(new TimerTask() { // @Override // public void run() { // System.out.println("test"); // } // },1000); //1秒之后每隔一秒执行run()方法 // timer.schedule(new TimerTask() { // @Override // public void run() { // System.out.println("test"); // } // },1000,1000); GregorianCalendar gregorianCalendar = new GregorianCalendar(2019, 9, 1, 19, 20,00); //Tue Oct 01 19:20:00 CST 2019 注意0是1月 timer.schedule(new TimerTask() { @Override public void run() { System.out.println("test"); } }, gregorianCalendar.getTime(),1000); }
复杂的任务调度
quartz框架(已经集成到spring中了)
如果需要系统学习这个框架,需要将这个框架下载下来,里面example中有使用案例;
使用maven下载依赖
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.1</version> </dependency>
基本使用
实际过程中直接使用,不需要写,example中有使用案例。
public class Demo { public static void main(String[] args) { //创建Scheduler工厂 StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory(); //从工厂获得调度器 try { Scheduler scheduler = stdSchedulerFactory.getScheduler(); //设置时间规则 Date date = DateBuilder.evenSecondDateAfterNow(); //设置执行的工作 JobDetail jobDetail= JobBuilder.newJob(Test.class).withIdentity("test").build(); //设置触发条件 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger").startAt(date).build(); //注册任务和条件 scheduler.scheduleJob(jobDetail,trigger); scheduler.start(); //scheduler.shutdown(); } catch (SchedulerException e) { e.printStackTrace(); } } }
必须是一个public类
public class Test implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println("started"); } }
补充
//每隔2秒执行一次,重复3次(一共会重复4次) Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger").startAt(date).withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).withRepeatCount(3)).build();
HappenBefore
你写的代码很可能根本没按你期望的顺序执行,因为编译器和CPU会尝试重排指令使得代码更快地运行,称为指令重排。
前提是数据之前没有依赖,可能后面的代码会先执行
首先自己写的代码会被编译成机器指令(其中的变量会被决定有那个寄存器存储)
从内存中获取一条指令。从对应的寄存器中获取相应的数据值(copy一份到工作内存),cpu计算结果,同步到主存中(这一步可能会出现问题,原因同步比较慢)
同步数据慢:造成数据不一致
数据之间没有依赖:操作指令重排
public class Demo { private static int a=0; private static Boolean flag=false; public static void main(String[] args) { //如果没有出错,可以将循环次数加大 for (int i = 0; i < 100; i++) { a = 0; flag = false; Thread t1 = new Thread(() -> { a = 1; flag = true; }); Thread t2 = new Thread(() -> { if (flag){ a*=1; } //按照预期值a是不可能是0的 if (a==0){ System.out.println("happed before-->a="+a); } }); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
ThreadLocal
即使定义在成员变量中,所有的线程对他的操作相互不影响,都把他当做了局部变量。
public class Demo { private volatile static int i = 0; private static ThreadLocal threadLocal1 = new ThreadLocal<Integer>(); private static ThreadLocal threadLocal2 = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 0; } }; public static void main(String[] args) throws InterruptedException { //默认初始化为null; System.out.println(threadLocal1.get()); //重写initialValue方法,赋给初始值. System.out.println(threadLocal2.get()); new Thread(()->{ threadLocal1.set(11); System.out.println(threadLocal1.get()); }).start(); new Thread(()->{ System.out.println(threadLocal1.get()); }).start(); } }
InheritableThreadLocal
和ThreadLocal用法一样,只是子线程会继承父线程的数据,但是子线程修改数据不会影响父线程,一旦子线程修改了了数据,父线程在修改数据就不会影响子线程了。