Java多线程复习与巩固(二)--线程相关工具类的使用
来源:互联网 发布:2016年免费的顶级域名 编辑:程序博客网 时间:2024/06/05 19:10
定时器(Timer类)
如果我们需要让某个任务在另一个线程中周期性的执行,或者让它在某个时刻执行一次。这时我们可能会写这样的代码:
周期任务:
// 周期任务public class PeriodTask implements Runnable{ private long period = 1000; private boolean running = true; public void cancel(){ running = false; } public void setPeriod(long period){ this.period = period; } public long getPeriod(){ return this.period; } public void run(){ while(running){ try{ Thread.sleep(period); }catch(InterruptException e){} task... } }}
定时任务:
// 定时任务public class TimeTask implements Runnable{ private long delay; public void setDelay(long delay){ this.delay = delay; } public long getDelay(){ return this.delay; } public void run(){ try{ Thread.sleep(delay); }catch(InterruptException e){} task... }}
这就像Linux中的crontab
命令和at
命令一样,一个周期执行n次,一个定时执行一次。
而Java中已经将这些功能封装在一个Timer中。
但是TaskQueue
和TimerThread
我们是看不到的,我们能用的只有Timer
和TimerTask
这两个类。
TimerTask
就表示我们要执行定时的任务,它有四种状态:
Virgin:代表TimerTask刚创建,没有被添加到定时器中。
Scheduled:代表该TimerTask已经添加到Timer的计划表中了(被添加到TimerQueue中)
Cancelled:代表该TimerTask已经被取消,不能再被Timer定时器调用。
Executed:代表该TimerTask已经被Timer执行完了。
状态之间的转换图如下:
除了上图出现的三个可用的方法,Timer中还有一个Timer.purge方法,它负责将TimerQueue中已经取消的任务清除掉,也就是把Cancelled状态的任务清除。另外如果你没调用这个方法清除Cancelled状态的任务,TimerThread会自动帮你把Cancelled和Executed状态的TimerTask任务清除掉。
示例:
import java.util.Timer;import java.util.TimerTask;public class TimerTest { static void printMessage(String msg) { long currTime = System.currentTimeMillis(); System.out.println(currTime + ": " + msg); } static class Task1 extends TimerTask { public void run() { printMessage("task1"); } } static class Task2 extends TimerTask { public void run() { printMessage("task2"); } } static class Task3 extends TimerTask { public void run() { printMessage("task3"); } } public static void main(String[] args) throws InterruptedException { Timer timer = new Timer(); TimerTask task1 = new Task1(); timer.schedule(task1, 3000);// 延迟三秒执行 printMessage("task1 scheduled"); TimerTask task2 = new Task2(); timer.schedule(task2, 2000, 1000);// 2秒后以1秒周期执行 printMessage("task2 scheduled"); TimerTask task3 = new Task3(); timer.schedule(task3, 1000, 2000);// 1秒后以2秒周期执行 printMessage("task3 scheduled"); Thread.sleep(6000);// 主线程睡眠十秒 task2.cancel();// 取消定时任务二 timer.purge(); printMessage("task2 cancelled"); Thread.sleep(6000);// 主线程睡眠6秒 task1 = new Task1(); timer.schedule(task1, 2000, 1000);// 2秒后以1秒周期执行 printMessage("task1 scheduled again"); Thread.sleep(6000); timer.cancel(); }}
虽然Java 5.0以后有了ScheduledThreadPoolExecutor也能进行定时任务的执行,但是它是用线程池实现的(Timer是单线程的),对于一些小功能来说没必要使用线程池,Timer足以应付。关于ScheduledThreadPoolExecutor等更多线程池相关的类会在稍后的文章中讲到。
线程局部变量(ThreadLocal类)
线程局部变量用于为每个线程维护一个变量的副本,使得每个线程都可以访问自己线程中的副本对象,而不会对其他线程中的副本造成影响,而且在访问这些变量时为我们提供了统一的访问方式。线程局部变量ThreadLocal有一个子类:InheritableThreadLocal。InheritableThreadLocal类会继承父线程的已经存储的副本,也就是说子线程会和父线程共享父线程中已有的副本,但这也使得子线程访问父线程要考虑同步问题,所以这个类实际上也不常用。
下面先看一张Thread与ThreadLocal之间的关系图,ThreadLocalMap以ThreadLocal对象作为Key,将线程要存的副本作为Value(ThreadLocalMap是本质上是一个哈希表,与HashMap类不同,它以“再哈希法”来解决哈希碰撞的问题):
ThreadLocal类很简单只有四个可用的方法initialValue
, get
, set
, remove
:
// 这个方法需要我们继承ThreadLocal然后进行重写。 protected T initialValue() { return null; } public T get() { Thread t = Thread.currentThread(); //获取当前线程的ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { // 以ThreadLocal为Key,取出Entry ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") // 获取Entry中的Value值 T result = (T)e.value; return result; } } // 为空时对变量进行初始化 return setInitialValue(); } private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) // 如果当前线程已经有ThreadLocalMap则直接将value存进入 map.set(this, value); else // 当前线程还没有ThreadLocalMap对象,则创建一个。 createMap(t, value); } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) // 删除当前线程中当前ThreadLoca对应的Entry。 m.remove(this); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
示例:
示例一:
import java.util.concurrent.atomic.AtomicInteger;public class ThreadLocalTest { static class ClientIdGenerator extends ThreadLocal<Integer> { AtomicInteger clientCount = new AtomicInteger(); @Override protected Integer initialValue() { return clientCount.incrementAndGet(); } } public static void main(String[] args) { ClientIdGenerator clientId = new ClientIdGenerator(); for (int i = 0; i < 10; i++) { new Thread(new Runnable() { public void run() { try { Thread.sleep(10); } catch (InterruptedException e) {} System.out.println(Thread.currentThread().getName() + ": I am client" + clientId.get()); } }).start(); } }}
实例二:
Spring中日期转换工具
public class DateConverter implements Converter<String, Date> { private static final String PATTERN = "yyyy-MM-dd HH:mm:ss"; private static final ThreadLocal<SimpleDateFormat> LOCAL = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat(PATTERN); } }; public Date convert(String source) { try { return LOCAL.get().parse(source); } catch (ParseException e) { e.printStackTrace(); } return null; }}
- Java多线程复习与巩固(二)--线程相关工具类的使用
- Java多线程复习与巩固(一)--线程基本使用
- Java多线程复习与巩固(三)--线程同步
- Java多线程复习与巩固(六)--线程池ThreadPoolExecutor
- Java多线程复习与巩固(四)--synchronized的实现
- Java多线程复习与巩固(七)--原子性操作
- Java多线程复习与巩固(八)--volatile关键字与CAS操作
- Java语言基础复习与巩固
- Java多线程复习与巩固(五)-生产者消费者问题(第一部分)
- HTML复习与巩固
- 复习巩固java基础知识
- java多线程二(java线程池的分析和使用)
- [Java多线程 二]---线程的状态与基本操作
- 初见Java多线程(二、线程的创建与启动)
- [Java多线程 二]---线程的状态与基本操作
- 巩固 Java Future 的使用
- java基础巩固笔记(5)-多线程之线程并发库
- JAVA多线程基础知识复习二
- forward 和redirect权区别
- linu spi子系统驱动开发笔记之实例(2)
- odoo android 定制开发
- 通达OA 工作流流转过程中使用系统自带的提醒功能设置(图文)
- 2426: [HAOI2010]工厂选址 贪心
- Java多线程复习与巩固(二)--线程相关工具类的使用
- 智能输入法的实现
- 设计模式—享元模式(二十二)
- 抽屉式布局DrawerLayout的实现
- 建造者模式详解
- TensorFlow教程02:MNIST实验——Softmax回归
- iOS App Performance: Instruments & beyond
- Quartz中时间表达式的设置-----corn表达式
- Hadoop/Spark相关面试问题总结