多线程 04


1. 守护线程

  1. java语言中线程分为两大类:用户线程和守护线程(后台线程,最具代表性的是垃圾回收线程)
  2. 一般守护线程是一个死循环,所有的用户线程只要结束,守护线程自动结束
  3. 主线程main是一个用户线程
  4. 守护线程用在什么地方????

比如规定每天0点时,系统数据会自动备份,这时需要定时器,可以将定时器设为守护线程,一直守着,到0点准时备份一次

package 多线程;

/*用户线程只要结束,守护线程自动结束,即使守护线程是死循环*/
public class T3 {

	public static void main(String[] args) {
		Thread thread = new MyThead1();
		thread.setName("备份数据的线程");
		//设为守护线程
		thread.setDaemon(true);
		
		thread.start();
		

		// 主线程 :是用户线程
		for (int i = 1; i < 6; i++) {
			System.out.println(Thread.currentThread().getName() + "-->" + i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

//线程1
class MyThead1 extends Thread {
	int i = 0;

	public void run() {
		while (true) {
			System.out.println(Thread.currentThread().getName() + "--->" + (++i));
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}
}
/*main-->1
备份数据的线程--->1
main-->2
备份数据的线程--->2
main-->3
备份数据的线程--->3
main-->4
备份数据的线程--->4
main-->5
备份数据的线程--->5
备份数据的线程--->6*/

2. 定时器

  1. 就是间隔特定的时间执行特定的程序
  2. 可以使用:

(1)使用sleep方法,设置睡眠时间

(2)在java的类库中,可以直接使用 java.util.Timer ,不过在实际开发中也很少用,许多高级框架都是支持定时任务的;

在实际开发中,使用较多的是Spring 框架中提供的SpringTask 框架,经过简单配置,就可以实现定时任务。(其底层就是java.util.Timer)

  1. 了解 java.util.Timer的原理
package 多线程;
/*
 * 1. Timer()构造方法,创建一个计时器 
 * 2. public void schedule(TimerTask task, Date firstTime, long period)
 *    此方法实现: 安排指定的任务在指定的时间开始进行重复的固定延迟执行
 *    3个参数: firstTime就是任务第一次执行的时间   period延迟多久执行一次,单位毫秒
 *          public abstract class TimerTask implements Runnable {
 *          TimerTask实现Runnable接口,就是一个线程
 *          注意:TimerTask是一个抽象类,要实现此类,得到任务对象
 *3. 也可以将定时器设为守护线程: 就是在构造方法 Timer(true) */
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class T3 {

	public static void main(String[] args) throws ParseException {
		//创建定时器对象
		 Timer  timer  = new Timer();
		//指定定时任务
		   //第一次执行的时间 
		 SimpleDateFormat date = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
		 Date firstTime  =  date.parse("2022-04-15 16:15:26");
		  //定时任务对象,因为是一个抽象类,不可以直接new 对象,单独编写一个定时任务类
		 timer.schedule(new LogTimer(),firstTime,3000);
	}
}

//定时任务类
class LogTimer extends TimerTask{

	@Override
	public void run() {
		 //在此编写需要执行的任务
		 SimpleDateFormat date = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
		 String string  = date.format(new Date());//得到当前时间
		System.out.println("当前时间"+string+"完成一次数据备份");
	}	
}

3. 实现线程的第三种方式:实现Callable接口(JDK8新特性)

这种方式实现的线程可以获取线程的返回值,之前的两种方式:

(1)继承Thread类,实现run方法

(2)实现Runnable接口 ,实现run方法,在将其new 对象放到Thread的构造方法中

中的实现run方法的返回值都是void

package 多线程;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class T3 {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// 第一步:创建一个未来任务类对象
		// 需要一个Callable参数,可以使用匿名内部类实现Callable接口
		FutureTask task = new FutureTask(new Callable() {
			@Override
			public Object call() throws Exception {// call方法就相当于run方法,只不过有返回值
				System.out.println("call方法开始执行---");
				Thread.sleep(3000);
				System.out.println("call方法结束执行---");
				return 1 + 2;
			}
		});

		// 第二步:创建线程对象
		Thread thread = new Thread(task);
		// 启动线程
		thread.start();
		// 第三步:在主线程main中如何获取thread线程的返回值??
		Object object = task.get();
		// main方法这里的程序要想执行,必须等待get()方法的结束,而此方法是需要另一个线程的执行,可能很久,导致阻塞当前线程(主线程)
		System.out.println("main线程end---");
	}
}

优点:获取线程的返回值

缺点:效率比较低,在获取某个线程的执行结果时,当前线程受阻塞。

4. 关于Object类中的wait 和 notify 方法

  1. wait 和 notify 方法不是线程对象的方法,而是java中任何一个java对象都有的方法,它们是Object类中自带的。

  2. wait 方法作用:

    Object o = new Object( );

    o.wait( );

    表示:让正在o对象上活动的线程进入等待状态,无期限等待,直到被唤醒为止。

  3. notify 方法作用:

? Object o = new Object( );

? o. notify( );

? 表示:让正在o对象上等待的线程被唤醒,进入活动状态 。

而 notifyAll( ) 是唤醒o对象上所有处于等待的线程。

  1. 生产者和消费者模式:

针对特定需求:

一个线程负责生产,一个线程负责消费,而且达到生产和消费均衡

类比于一个仓库生产和消费产品:

因为仓库是多线程共享的,所以要考虑仓库的线程安全问题; 仓库对象最终调用wait 和 notify 方法 ,而wait 和 notify 方法 建立在synchronized 线程同步的基础上。

o.wait () 方法会让正在o对象上活动的当前线程进入等待状态,并且释放之前占有的o对象的锁。

o. notify( )方法只会通知,不会释放之前占有的o对象的锁。

package 多线程;
//1. 使用Object类中的wait 和 notify 方法实现“生产者和消费者模式”
//2. “生产者和消费者模式”是指一个线程负责生产,一个线程负责消费,而且达到生产和消费均衡
//3. wait 和 notify 方法 建立在synchronized 线程同步的基础上。

/*模拟:
 * 仓库使用List集合,并且只能存储一个元素
 * 1个元素代表仓库满了  0个就空
 * 达到:生产一个消费一个:假设生产线程执行,则消费线程等待
 * */
import java.util.ArrayList;
import java.util.List;

public class T3 {

	public static void main(String[] args) {
		// 创建仓库对象,是共享的
		List list = new ArrayList();
		// 创建生产线程对象
		Thread t1 = new Thread(new Producer(list));
		t1.setName("生产者线程");
		t1.start();
		// 创建消费线程对象
		Thread t2 = new Thread(new Consumer(list));
		t2.setName("消费者线程");
		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) {
					// 仓库已经满了,当前线程进入等待状态,并且释放Producer之前占有的list集合的锁。若不释放,另一个线程就无法对仓库的修改数据
					try {
						list.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				// 程序到此,表示仓库是空的,可以生产
				Object object = new Object();
				list.add(object);
				System.out.println(Thread.currentThread().getName() + "生产" + object);
				// 唤醒消费者进行消费
				list.notify();
			}
			//当代码执行到此处,也会释放Producer之前占有的list集合的锁
		}
	}
}

//消费线程
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) {
					// 仓库已经空了,当前线程进入等待状态,并且释放Consumer之前占有的list集合的锁。
					try {
						list.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				// 程序到此,表示仓库是满的,可以消费
				Object object = list.remove(0);
				System.out.println(Thread.currentThread().getName() + "消费" + object);
				// 唤醒生产者进行生产
				list.notify();
			}
		}
	}
}
/*消费者线程消费java.lang.Object@2a1ff8be
生产者线程生产java.lang.Object@4318d75f
消费者线程消费java.lang.Object@4318d75f
生产者线程生产java.lang.Object@2d25a011
消费者线程消费java.lang.Object@2d25a011
生产者线程生产java.lang.Object@72f744c6
消费者线程消费java.lang.Object@72f744c6
.....*/

相关