笔记|Day30.守护线程、定时器、消费者模式 | wait、notify方法

目录
守护线程
定时器(Timer)
实现线程的第三种方式 :实现Callble接口(JDK8新特性)
生产者和消费者模式
???????
守护线程

  • Java语言中线程分为两大类:
一类是:用户线程; 一类是:守护线程(后台线程);
注意:主线程main方法是一个用户线程。
守护线程中最具有代表性的就是:垃圾回收线程
  • 守护线程的特点
守护线程一般是一个死循环,所有的用户线程只要结束,守护线程自动结束
  • 守护线程用在什么地方呢?
如:每天24点时,系统数据自动备份
这个需要使用到定时器,我们可以将定时器设置为守护线程。
如果结束了,守护线程自动退出,没有必要进行数据备份了。
  • 方法
void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
/*守护线程*/ public class ThreadTest11 { public static void main(String[] args){ Thread t = new BakDataThread(); t.setName("备份数据的线程"); //启动线程之前,将线程设置为守护线程 t.setDaemon(true); t.start(); //主线程:用户线程 for(int i = 0; i<10 ; i++){ System.out.println(Thread.currentThread().getName() + "--->"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } class BakDataThread extends Thread{ public void run(){ int i = 0; while (true){ System.out.println(Thread.currentThread().getName() + "--->" + (++i)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }

定时器(Timer)
  • 定时器的作用:
间隔特定的时间,执行特定的程序。
每周尽心银行账户的总账操作;每天进行数据的备份操作;
在实际开发中,这种需求很常见。
在java中,定时器可以使用多种方式实现
使用sleep方法,设置睡眠时间,到某个时间点醒来,执行任务。这种方式是最原始的定时器。
在java中的类库中已经写好了一个定时器:java.util.Timer,可以直接拿来用。这种方式在开发中也很少用,因为现在又很多的高级框架都是支持定时任务的。
实际开发中,使用较多的是Spring框架提供的SpringTask框架,这个框架只要进行简单的配置,就可以完成定时器任务。
  • 构造方法
Timer()
创建一个新计时器。
Timer(boolean isDaemon)
创建一个新计时器,可以指定其相关的线程作为守护程序运行。
Timer(String name)
创建一个新计时器,具有指定的名称。
Timer(String name, boolean isDaemon)
创建一个新计时器,具有指定的名称,并且可以指定作为守护程序运行。
  • 方法
void schedule(TimerTask task, Date time)
安排在指定的时间执行指定的任务。
void schedule(TimerTask task, Date firstTime, long period)
安排指定的任务在指定的时间开始进行重复的固定延迟执行
void schedule(TimerTask task, long delay)
安排在指定延迟后执行指定的任务。
void schedule(TimerTask task, long delay, long period)
安排指定的任务从指定的延迟后开始进行重复的固定延迟执行
void cancel()
终止此计时器,丢弃所有当前已安排的任务。
int purge()
从此计时器的任务队列中移除所有已取消的任务。
  • 代码实现
/*使用定时器指定定时任务*/ public class TimerTest { public static void main(String[] args) throws ParseException { //创建定时器对象 Timer timer = new Timer(); //Timer timer = new Timer(true); 守护线程的方式//第一次执行时间 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date firstTime = sdf.parse("2022-01-15 11:37:00"); //指定定时任务 //timer.schedule(定时任务,第一次执行时间,间隔多久执行一次); timer.schedule(new LogTimerTask(),firstTime,1000*10); } } //编写一个定时任务类:假设这是一个记录日志的定时任务 class LogTimerTask extends TimerTask{ @Override public void run() { //编写需要执行的任务 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String strTime = sdf.format(new Date()); System.out.println(strTime+ "完成了一次数据备份!"); } }

笔记|Day30.守护线程、定时器、消费者模式 | wait、notify方法
文章图片

实现线程的第三种方式 :实现Callble接口(JDK8新特性) 思考:系统委派一个线程去执行一个任务,该线程执行完任务之后,可能会有一个执行结果,我们怎么能拿到这个执行结果呢?
  • 实现Callble接口:
    优点:这种方式实现的线程可以获取线程的返回值
    缺点:这种方式执行效率较低,在获取线程执行结果时,当前线程会受到阻塞。
/*实现线程的第三种方式: 实现Callable接口*/ public class ThreadTest12 { public static void main(String[] args) throws ExecutionException, InterruptedException { //第一步: 创建一个"未来任务类"对象 (以下使用了匿名内部类的方式) //参数非常重要,需要给一个Callble接口实现类对象 FutureTask task = new FutureTask(new Callable() { @Override public Object call() throws Exception { //cal; ()方法相当于run方法 //线程执行任务,执行之后可能会有一个执行结果 System.out.println("call method begin"); Thread.sleep(1000*10); System.out.println("call method end"); int a = 100; int b = 200; return a+b; //自动装箱 } }); //创建线程对象 Thread t = new Thread(task); //启动线程 t.start(); //在主线程中,怎么获取t线程的返回结果? Object obj = task.get(); //这里的程序执行必须要等待get()方法执行结束, //而gat方法拿到执行结果需要等task线程执行结束 //所以线程会受阻,进入阻塞状态} }

  • FutureTask类方法
FutureTask(Callable callable)
创建一个 FutureTask,一旦运行就执行给定的 Callable。
FutureTask(Runnable runnable, V result)
创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 。
V get()
等待计算完成,然后获取其结果。
V get(long timeout, TimeUnit unit)
最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
boolean isDone()
如果任务已完成,则返回 true。
boolean cancel(boolean mayInterruptIfRunning)
试图取消对此任务的执行。

生产者和消费者模式
  • wait 和 notify方法
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void wait(long timeout)
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
void notify()
唤醒在此对象监视器上等待的单个线程。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
  • wait和notify建立在synchronized线程同步的基础之上
  • wait和notify是任何一个java对象都有的方法,不通过线程对象调用
o.wait()方法会让正在o对象上活动的当前线程进入等待状态,并且释放之前占有的o对象的锁。
o.notify()方法只会将线程唤醒,不会释放之前占用的o对象的锁。
  • 生产者和消费者模式
笔记|Day30.守护线程、定时器、消费者模式 | wait、notify方法
文章图片


生产者和消费者模式要解决的问题就是生产者和消费者之间处理数据的动态平衡问题。
  • 代码实现
public class ThreadTest13 { public static void main(String[] args) { //创建1个仓库对象,共享的 List list = new ArrayList(); //创建生产者、消费者线程对象 Thread t1 = new Thread(new Producer(list)); Thread t2 = new Thread(new Consumer(list)); t1.setName("生产者线程"); t2.setName("消费者线程"); t1.start(); t2.start(); } }

//生产线程 class Producer implements Runnable{ //仓库 private List list; public Producer(List list) { this.list = list; } @Override public void run() { //一直生产(使用死循环模拟) while (true){ //给仓库对象List加锁 synchronized (list){ if(list.size() >0){//仓库已经有1个元素了 try { //当前线程进入等待,并且释放list集合的锁 list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //程序执行到此处说明仓库是空的,可以生产 Object obj = new Object(); list.add(obj); System.out.println(Thread.currentThread().getName() + "--->" +obj); //唤醒消费者,进行消费 list.notify(); } } } }

//消费线程 class Consumer implements Runnable{ //仓库 private List list; public Consumer(List list) { this.list = list; }@Override public void run() { //一直消费 while(true){ synchronized (list){ if(list.size()==0){ //仓库已经空了,消费者线程等待,释放掉list集合的锁 try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //程序能够组合性到次数说明仓库中有数据,进行消费 Object obj = list.remove(0); System.out.println(Thread.currentThread().getName() + "--->" + obj); //唤醒生产者,进行生产 list.notify(); } } } }

如果想每次生产3个消费3个,需要修改生产线程进入等待条件(list.size() ==0),给生产、消费线程唤醒方法添加条件
//生产线程 { @Override public void run() { //一直生产(使用死循环模拟) while (true){ //给仓库对象List加锁 synchronized (list){ if(list.size() ==3){//仓库已经有3个元素了,进入等待list.wait(); ... } 生产... if(list.size() ==3){//已经生产了3个,唤醒消费线程 list.notify(); } } } } }

//消费线程 { @Override public void run() { while(true){ synchronized (list){ if(list.size()==0){ //仓库已经空了,消费者线程等待,释放掉list集合的锁 ... list.wait(); ... } 消费... if(list.size()==0){//消费空了,唤醒生产者,进行生产 list.notify(); } } } } }

【笔记|Day30.守护线程、定时器、消费者模式 | wait、notify方法】

    推荐阅读