Day22
一、线程通信
1、生产者和消费者(会产生IllegalMonitorStateException异常,应该加上synchronized)
package com.yxlim.day22.work;
/**
* 生产者和消费者只能有一个线程
*/
public class Test4 {
public static void main(String[] args) {
Object obj = new Object();
new Producter(obj).start();
new Customer(obj).start();
}
}
//产品
class Product {
//定义一个静态变量(生产者和消费者都访问这个变量)
public volatile static String values;
}
//生产者
class Producter extends Thread {
private Object obj;
//使用构造器传入obj实现线程等待wait()、notify()
public Producter(Object obj) {
this.obj = obj;
}
@Override
public void run() {
int sum = 0;
while (true) {
synchronized (obj) { //加锁,防止调用wait,notify异常
if (Product.values == null) {
try {
//线程休眠,让产生数据更加明显
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//使用系统当前的毫秒值当做产品
Product.values = System.currentTimeMillis() + "";
System.out.println("生产者生产的产品:" + Product.values);
sum++; //结束线程标志
obj.notify();//唤醒线程
} else {
try {
//线程等待
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (sum == 50) {//循环进行50次
break;
}
}
}
}
}
class Customer extends Thread {
private Object obj;
//使用构造器传入obj
public Customer(Object obj) {
this.obj = obj;
}
@Override
public void run() {
int sum = 0;
while (true) {
synchronized (obj) {
if (Product.values != null) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者消费的产品:" + Product.values);
Product.values=null;
sum++;
obj.notify();
} else {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (sum == 50) {
break;
}
}
}
}
}
2、使用的方法(都是Object类的方法):
- wait():线程等待,可以添加参数,表示等待多长时间,不加参数,需要被唤醒;
- notify():线程唤醒,唤醒当前处于等待的线程;
- notify():唤醒所有的线程;
wait()和sleep()方法的区别:
- wait()表示线程等待池等待,出让系统资源(释放锁对象),其他线程可以占用CPU;
- sleep()表示线程不让出资源(不释放锁对象),其他线程无法占用CPU;
3、生产者和消费者模式主要避免资源的浪费,但会产生死锁的情况,主要是两个线程互相不释放对方的锁对象或不将条件满足的线程唤醒
二、线程的生命周期
1、Thread中有一个Thread.State内部类,主要存储线程的状态
- NEW:至今尚未启动的线程处于这种状态;
- RUNNABLE:正在 Java 虚拟机中执行的线程处于这种状态;
- BLOCKED:受阻塞并等待某个监视器锁的线程处于这种状态;
- WAITING:无限期地等待另一个线程来执行某一特定操作的线程处于这种状态;
- TIMED_WAITING:等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态;
- TERMINATED:已退出的线程处于这种状态;
2、public Thread.State getState():获取线程的状态
三、使用线程池实现多线程
1、线程池的*接口Executor《翻译:执行者》,我们所说的线程池是ExecutorService,它是继承Executor*接口的接口;
2、Executors提供了4种常规的方法返回线程池ExecutorService
- public static ExecutorService newCachedThreadPool()
- public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
- public static ExecutorService newFixedThreadPool(int nThreads)(常规)
- public static ExecutorService newFixedThreadPool(int nThreads,ThreadFactory threadFactory)
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize,ThreadFactory threadFactory)
- public static ExecutorService newWorkStealingPool()
- public static ExecutorService newWorkStealingPool(int parallelism)
3、AbstractExecutorService是实现ExecutorService的抽象类,而主要使用的是ThreadPoolExecutor,它是继承抽象类的子类
- 主要使用的方法void execute(Runnable command):在将来的某个时间执行给定的命令
4、主要创建线程池并使用的方式:
//创建线程池
ExecutorService es=Executors.newFixedThreadPool(2);
//使用submit方法提交任务(实现Callable或Runnable接口的子类),并使用Futrue类接收
Future<Integer> f=es.submit(new MyCallable(5));
//调用Future的get()方法获取Callable子类传回的值
System.out.println(f.get());
//关闭线程池
es.shutdown();
四、Timer(定时器)普通类
1、常规方法:
- public void schedule(TimerTask task, Date time):在指定的时间安排指定的任务执行
2、TimerTask是抽象类,实现了Runnable接口,但不重写run方法,让继承它的子类实现
五、单例设计模式
1、单例模式表示这个类在内存里只加载一次,大部分工具类都是单例设计模式
2、单例模式实现的步骤:
- 私有的对象(类自身的对象)属性,外部不能访问
- 私有的构造器,不让外部实例化这个类
- 提供公有的方法,让外界的类进行访问
3、单例实现的方式:
- 饿汉模式
//私有对象属性
private static DateUtils utils=new DateUtils();
//私有构造器
private DateUtils(){}
//给外部访问的方法
public static DateUtils getInstance(){
return utils;
}
- 懒汉模式
//私有对象属性
private static StringUtils utils;
//私有一个构造器
private StringUtils() {}
//给外部使用的方法(加锁防止多线程异常)
public synchronized static StringUtils getInstance() {
if (utils == null) {
utils = new StringUtils();
}
return utils;
}
- double lock(双重锁)
//私有对象属性
private static FileUtils utils;
//私有构造
private FileUtils(){}
//提供公有方法
public synchronized static FileUtils getInstance(){
if(utils==null){
synchronized (FileUtils.class){
if(utils==null){
utils=new FileUtils();
}
}
}
return utils;
}
六、简单工厂模式(多态体现的利用《父类为返回值、父类为参数》)
1、不用接口,直接用一个工厂生产各种类型的产品(Factory类内直接写get方法)
2、使用接口,创建工厂接口,如果想生产铁,就创建钢铁工厂去实现工厂接口,然后实现get方法获取钢铁对象(以父类为返回值),提供一个标准
//工厂接口
interface IFactory {
Pet getPet();
}
//实现工厂接口
public class FactoryDog implements IFactory {
@Override
public Pet getPet() {
return new Dog();
}
}
//测试
public class Test1 {
public static void main(String[] args) {
FactoryDog fd=new FactoryDog();
fd.getPet().shout();
}
}
七、枚举enum
//创建枚举
public enum Week {
周一, 周二, 周三, 周四, 周五, 周六, 周日
}
//测试
public class Test4 {
public static void main(String[] args) {
new Test4().eat(Week.周一);
}
public void eat(Week week){
switch (week){
case 周一:
System.out.println("周一");
break;
case 周二:
System.out.println("周二");
break;
case 周三:
System.out.println("周三");
break;
case 周四:
System.out.println("周四");
break;
case 周五:
System.out.println("周五");
break;
case 周六:
System.out.println("周六");
break;
case 周日:
System.out.println("周日");
break;
}
}
}