总结
今天主要还是巩固了多线程的相关知识,了解到线程的生命周期,多线程运行的第三种方式-----线程池,生产者与消费者模式,单例设计模式和简单工厂者模式以及枚举类型是相关内容。
线程的生命周期![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6d0a2da81ec705d4955171bff826c6c3.png)
NEW 新建态
至今尚未启动的线程处于这种状态。
RUNNABLE 就绪态和运行态
正在 Java 虚拟机中执行的线程处于这种状态。
BLOCKED 阻塞态(等待锁和I\O)
受阻塞并等待某个监视器锁的线程处于这种状态。
WAITING 阻塞态1(调用了wait方法,等待其他线程唤醒的状态)
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
TIMED_WAITING 阻塞态2(调用了用时间限制的wait方法或者是sleep方法)
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
TERMINATED 死亡态
已退出的线程处于这种状态。
public Thread.State getState( ) //获取线程的状态
线程池
步骤:
- 创建一个实现类实现接口Callable;
- 实现Callable里面的run( )方法(此方式的run方法有返回值);
- 实例化一个ExecutorService得到线程池的对象;
语法:
ExecutorService es = Executors.newFixedThreadPool(线程数量)//得到线程池的对象
- 使用这个对象调用实现类里面重写的run方法;
- 实例化一个Futrue对象接受run方法的返回值;(这个过程叫做异步回调)
- 使用shutdown()结束这个线程;
生产者与消费者模式
生产者与消费者模式的实际问题是指两个线程不能同时进行,一个线程正在运行另外一个线程必须等待资源;要实现这样的操作必须使用同步锁(对象锁)
需要使用的几种方法
- Object里面的方法:
- wait( ) 等待线程(可以传参也可以不传参)
- notify()唤醒当前睡眠的线程
- notifyall()唤醒所有睡眠的线程
是Object方法的原因:这些方法都依赖于对象锁,对象是任意的对象,能够表示任意的对象的类只有Object
- wait( ) 与 sleep() 的区别 sleep() 在一段时间会释放锁 , wait()可以一段时间类释放锁,也可以永久不释放;
- notify() 与 notifyAll( ) 的区别 : notify 唤醒当前睡眠的对象,如果这个睡眠对象有多个,会交替的唤醒;notifyAll 唤醒所有睡眠的线程
public class demo {
//肯定要有一个产品类(内部类)
static class Product{
//定义一个变量来记录产品的信息
//volatile 这个关键字的作用是 ,
//也就是a线程的数据发生改变,b线程可以立马知道
public static volatile String values;
}
//定义一个生产者的类,实现多线程
static class Producter extends Thread{
// 定义一个产品
Object lock;
public Producter (Object lock){
this.lock =lock;
}
@Override
public void run() {
// 如果不为空,代表有产品,思考,能保证消费者与生产者的线程只有一个执行, 也就是要加锁
while(true){
synchronized (lock) {
if(Product.values !=null){
try {
lock.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//当前线程阻塞
//wait();
}
//生产产品(什么情况下生产产品)
Product.values="生产者生产的产品是:"+System.currentTimeMillis();
System.out.println(Product.values);
// 通知消费者进行消费(换醒其它线程)
lock.notify();
}
}
}
}
//消费者
static class Customer extends Thread{
// 也要提供一个产品进行操作
Object lock;
public Customer(Object lock){
this.lock = lock;
}
@Override
public void run() {
//关键点 消费产品(什么时候进行消费)
while(true){
synchronized (lock) {
if(Product.values ==null){
// 线程阻塞
try {
lock.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 不同于同null 消费
System.out.println("当前消费的产品是"+Product.values);
//消费完成之后,把产品设置为null
Product.values=null;
// 通知生产者进行生产
lock.notify();
}
}
}
}
// 这样就保证线程a 执行一次 , 线程b 执行了一次
public static void main(String[] args) {
Object object = new Object();
// 首先生产产品
new Producter(object).start();
// 调用消费者的线程
new Customer(object).start();
}
}
延时操作模式
public void schedule(TimerTask task, long delay, long period)
第一个参数传递了一个任务对象;第二个参数是隔多少秒来执行这个操作;第三个参数是每次间隔发送的时间(时间是以毫秒值为单位)
同时可以传递Date对象进行操作
TimerTask 这个是定时操作的任务对象
单例设计模式
设计模式 :
开发大佬,给的一些好的经验抽取
单例设置模式 :
有且仅加载一个类, 静态的也可以有且仅加载一次(出现问题比较多)
单例设计模式 :
只要是这个类在内存里只加载一次, 工具类大部分情况都是的是单例设计模式
单例设计模式的三个要素:
- 私有的属性 这个属性是当前类( 外部不能来访问这个属性)
- 私有的构造 不让外部来实例化这个类,使用私有的构造
- 公有的方法 : 提供一个对外的方法,让别人进行访问
各种模式
1. 恶汉模式 :
一开始就实例化出来 ,缺点超级多 :运行就卡 优点 :线程安全
private DateFormatUtils utils = new DateFormatUtils()
private DateFormatUtils(){
}
// 对外的方法
public DateFormatUtils getInstance(){
return utils
}
2. 懒汉 (比较懒)
懒加载 :不是一开始就加载 ,而是用的时候去加载
性能有所优化, 线程不安全
public class StringUtils {
// 私有的属性
private static StringUtils stringUtils;
//私有的构造
private StringUtils(){
}
// 在方法上加锁
public synchronized StringUtils getInstance(){
// 做一个判断是null判断
if(stringUtils ==null){
// 才实例化
stringUtils = new StringUtils();
}
return stringUtils;
}
3.老汉模式
不能被修改 ==> private final DateFormatUtils utils = new DateFormatUtils()
4.双重锁
public class FileUtils {
// 私有的属性
private static FileUtils fileUtils;
// 私有的构造
private FileUtils(){
}
// 提供公有的方法
public synchronized static FileUtils getInstance(){
if(fileUtils ==null){
//锁class 文件,防止高并发里遇到实例化两个对象,锁class文件
synchronized (FileUtils.class){
if(fileUtils == null){
fileUtils = new FileUtils();
}
}
}
return fileUtils;
}
}
简单工厂
简单工厂者模式:
生产产品 (对象) ==> 就是多态的一种体现
体现形式:
- 以父类作为返回值
工厂类
- 以父类作为参数
工厂接口
枚举类型
枚举类 :枚举类里都是常量
- 定义枚举的产量的时候 一般都是大写
- 每一个常量都是以逗号进行分割
定义:
是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内.
使用格式:
枚举类型通过enum语句来定义。
public enum Week {
MON("星期一"),
TUE("星期二"),
WED("星期三"),
THR("星期四"),
FRI("星期五"),
SAT("星期六"),
SUN("星期日");
private String day;
Week(String day) {
this.day = day;
}
public String getDay() {
return day;
}
public void setDay(String day) {
this.day = day;
}
}
返回此枚举常量的名称,在其枚举声明中对其进行声明。
public final String name()
返回带指定名称的指定枚举类型的枚举常量。名称必须与在此类型中声明枚举常量所用的标识符完全匹配。
public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name)
返回枚举常量的名称,它包含在声明中。
public String toString()
疑问
简单工厂者模式为什么一点都不感觉简单,反而觉得代码量十分大,在什么时候应用简单工厂者模式才是对的?