Java基础Day23

day23

  1. 死锁
  2. 死锁的发生原因:

在这里插入图片描述

代码
package com.zgjy.thread;
public class DeadLock {
public static void main(String[] args) {
Thread t0 = new Thread(“小瘦”) {

		@Override
		public void run() {// 死锁案例
			while(true) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized ("A") {
					System.out.println(getName() + "获取到A锁,等待B锁");
					synchronized ("B") {
					System.out.println(getName() + "获取到A锁,和B锁,休息去了");
					}
				}
			}
		}
	};
	t0.start();

Thread t1 = new Thread(“小胖”) {

		@Override
		public void run() {// 死锁案例
			while(true) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized ("B") {
					System.out.println(getName() + "获取到B锁,等待A锁");
					synchronized ("A") {
					System.out.println(getName() + "获取到A锁,和B锁,休息去了");
					}
				}
			}	
		}
	};
	t1.start();
}

}

  1. 如何尽量避免死锁问题:

  2. 不要使用嵌套的锁资源

  3. 不同的线程任务,尽量不要使用相同的锁

  4. 线程的状态
    一个线程从创建开始到线程死亡

  5. 新建

  6. 就绪

  7. 运行

  8. 阻塞

  9. 死亡

在这里插入图片描述

JDK中定义的线程状态:
Thread类中,有一个内部的枚举类型,表示线程的状态
在这里插入图片描述

• NEW----> 新建状态
至今尚未启动的线程处于这种状态。
• RUNNABLE—> 就绪和运行状态
正在 Java 虚拟机中执行的线程处于这种状态。
• BLOCKED—> 阻塞状态,等待锁对象资源
受阻塞并等待某个监视器锁的线程处于这种状态。
• WAITING—> 阻塞状态,wait方法无线等待
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
• TIMED_WAITING—> 阻塞状态,sleep休眠
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
• TERMINATED----> 死亡状态
已退出的线程处于这种状态。

  1. 线程池
    3.1 线程池的概述
    如果你的程序代码中,有很多的线程,每一个线程执行的任务,比较少,于是就需要频繁的在内存中给线程开辟独立空间,运行run方法,当run方法执行完毕,开辟的独立的内存空间就会变成垃圾,等待回收

线程从创建---->使用start开辟内存空间---->运行run—>线程死亡,销毁,回收内存,于是问题:频繁的创建和销毁线程,十分浪费时间和空间

为了解决上面描述的问题, 线程池的学习

什么是线程池:

线程池就是一个池塘,池塘中养了好多个线程,每一个线程都可以执行独立的线程任务,执行完任务,线程会到线程池中反复使用

在这里插入图片描述

3.2 线程池的实现过程

  1. 先创建出线程池,使用Executors类,来自于java.util.concurrent包,表示创建线程池的工厂类
  2. 使用Executors中静态方法,newFixedThreadPool(int thread) : 表示创建一个具有指定thread线程个数的线程池,方法的返回值类型ExecutorService ,是一个线程池的接口,能让线程池中的线程执行任务
  3. 使用ExecutorService 中方法submit(Runnable ran)
    submit(Runnable ran):
  1. 先检测线程池中是否具有空闲线程
  2. 如果有,将线程池中的线程获取到,执行ran对应的线程任务
  3. 执行任务结束,将线程归还到线程池中
  1. 线程池执行完任务后,代码没有结束,原因 :
    因为线程池反复利用,因此当所有的任务执行完毕,线程池仍然在等待执行线程任务,因此需要手动的将线程池销毁掉
    ExecutorService 中的shutDown() : 表示结束线程池,如果还有正在运行的线程,保证线程运行完毕,但是不接受新的线程任务了

代码
package com.zgjy.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {

public static void main(String[] args) {
	// 1. 创建线程池,Executors创建线程池的工厂类,就是创建池塘
	// 创建一个具有2个线程的线程池
	ExecutorService es = Executors.newFixedThreadPool(2);
	
	// 2. 定义一些线程任务,使用匿名内部类,实现Runnable接口
	Runnable ran = new Runnable() {

		@Override
		public void run() {
			System.out.println(5);
		}
	};
	
	Runnable ran1 = new Runnable() {

		@Override
		public void run() {
			System.out.println(10);
		}
	};
	Runnable ran2 = new Runnable() {

		@Override
		public void run() {
			System.out.println(15);	
		}
	};
	
	Runnable ran3 = new Runnable() {
		@Override
		public void run() {
			System.out.println(20);
		}
	};
	// 3. submit提交线程任务,并执行任务
	es.submit(ran);
	es.submit(ran1);
	es.submit(ran2);
	es.submit(ran3);
	
	// 4. 关闭线程池,销毁线程池,但是能保证正在运行的线程运行结束
	es.shutdown();
}

}

4.单例模式
Java中有23种设计模式,不同的设计模式具有不同的功能. 单例模式就是23种设计模式中的其中一种.

单例模式 : 指一个类型在内存中只有一个对象
单例模式使用案例 : 单例模式一般用作全局的使用类型.
例如 : 操作系统中,回收站功能就是一个单例模式,因为系统中的所有的累心文件的删除,都是删除到唯一的一个回收站中
操作系统中任务管理器,能统计当前操作系统中,所有正在运行的程序,每次有文件的添加运行或者是关闭,都是在同一个任务管理器中进行统计

单例模式的实现过程:

  1. 私有化构造方法
  2. 在类型中,创建唯一的一个类的对象
  3. 对外提供一个公共的获取唯一对象的方式

4.1 饿汉式
饿汉式表示: 表示非常迫切
实现思路: 创建一个类的时候,直接将这个类的唯一的一个对象确定好,不管这个对象是不是使用,饿汉式都非常急迫的将这个对象直接给你

实现过程:

  1. 构造方法私有化
  2. 类中创建一个私有的本类对象
  3. 提供一个对外公共访问唯一成员对象的方法

代码
package com.zgjy.singlon;
// 单例模式中的饿汉式 : 在内存中只有一个对象
public class Hungry {
// 1. 构造方法私有化
/*
* 修饰符 构造方法名(参数列表){
* // 通常对成员变量进行赋值
* }
*
* */
// 功能 : 除了本类之外,外类中不能创建对象
private Hungry() {

}

// 2. 在本类中常见一个对象
private static Hungry h = new Hungry();

// 3. 对外直接提供一个公共获取唯一对象的方法
public static Hungry getInstance() {
	return h;
}

}

package com.zgjy.singlon;
public class TestSinglon {
public static void main(String[] args) {
// 饿汉式的单例模式测试
Hungry h1 = Hungry.getInstance();
Hungry h2 = Hungry.getInstance();
Hungry h3 = Hungry.getInstance();
System.out.println(h1 == h2);// true
System.out.println(h2 == h3);// true
}
}

4.2 懒汉式
懒汉式: 非常懒, 如果要使用这个唯一的对象,才给你创建. 否则我不创建

实现过程:

  1. 私有构造方法
  2. 在成员位置声明一个私有对象(没有真是对象产生)
  3. 提供一个对外的公共的创建对象的方法
  1. 先判断下这个对象有没有,没有
  2. 如果没有对象,才给你创建对象

懒汉模式使用的注意:

  1. 创建对象时,需要先判断对象为null,才去创建对象
  2. 为了在多线程程序中,懒汉式仍然能保证只在内存中创建一个对象,因此添加了同步奥代码块,保证多个线程调用getInstance方法时,对象只创建一次

代码
package com.zgjy.singlon;
// 单例模式中的懒汉模式: 让类型在内存中只有一个对象
public class Lazy {
// 1. 私有构造,为了在其他类中,不能创建对象
private Lazy() {

}

// 2. 在本类的成员位置,只做一个本类对象的声明
// 成员变量,JVM自动的给赋初值,为null
// 所有引用数据类型的默认值都是null
private static Lazy l ;

// 3. 提供对外的公共的创建唯一对象的方式
public static Lazy getInstance() {
  synchronized ("A") {
	  if( l == null) {
			// 两个线程都在第18行被别人抢走CPU的资源
			// A 等着  B等着,因此需要同步代码块控制线程安全
			l = new Lazy();
		}
  }
	return l;
}

}

package com.zgjy.singlon;
public class TestSinglon {
public static void main(String[] args) {
//懒汉式的案例模式测试
Lazy l = Lazy.getInstance();
Lazy l1 = Lazy.getInstance();
Lazy l2 = Lazy.getInstance();
System.out.println(l == l1);// true
System.out.println(l1 == l2);// true
}
}

4.3老汉式
实现过程:

  1. 私有化构造方法,为了在其他类中不能直接创建对象
  2. 在类中,提供使用public static final 修饰的类型对象

代码
package com.zgjy.singlon;
// 单例模式老汉式实现方式: 类型在内存中只有一个对象
public class Old {
// 1. 私有构造方法
private Old() {

}

// 2. final修饰的变量称为常量
public final static Old O = new Old();

}

package com.zgjy.singlon;
public class TestSinglon {
public static void main(String[] args) {
//老汉式的单例模式测试
Old o1 = Old.O;
Old o2 = Old.O;
Old o3 = Old.O;
System.out.println(o1 == o2);// true
System.out.println(o2 == o3);// true
}
}

4.4单例实现方式的比较
使用原因:

  1. 饿汉式 : 类一经进入到内存,就会马上创建出一个对象,对象占有内存空间
  2. 懒汉式 : 类一经进入到内存,没有创建对象,也没有占有内存空间,真正要是用对象时,才在内存中开辟空间

饿汉式占用空间,节省时间
懒汉式开始不占用空间,但是时间使用比较多
饿汉式用空间换时间; 懒汉式用时间换空间,因此实际开发中,看中时间还是空间决定使用哪种方式

实际开发中,时间节省更重要,因此饿汉式使用更多,老汉使用也一样是很优秀的

  1. 枚举类型
    5.1 枚举类型的概述

  2. 枚举类型使用 enum 关键字定义的
    修饰符 class 类名{}
    修饰符 interface 接口名{}
    修饰符 enum 枚举名{}

  3. 枚举的使用场景
    有些情况下,类型中可以创建的对象的个数是固定的
    举例 : 星期类型----> 只有周一到周日,一共只有7天---->星期类型的对象只能有7个,7个对象分别对应周一到周日
    月份类型-----> 只有12个月---->对应只能创建12个对象,对应1月到12月

  4. 枚举类型,源文件.java , 编译后的文件.class

5.2 枚举类型的第一种实现方式

  1. 要求 : 使用老汉式的实现方式,定义星期类型中,只能创建1-7的7个对象
  2. 要求 : 使用类型实现星期中只能定义7个星期对象
  1. 使用enum关键字,创建一个枚举类型
  2. 将需要创建的对象名字,写在枚举类型中即可.这些对象名字就叫做枚举项,枚举项可以有多个,每一个枚举项就表示一个枚举类型对象,多个枚举项之间使用逗号分隔,最后一个枚举项使用;结尾
  3. 枚举项必须写在枚举类型的第一行

老汉式实现代码
package com.zgjy.enumDemo;
// 老汉式方式实现创建7个星期对象
public class WeekDayOld {

// 1. 构造方法私有化
private WeekDayOld() {
	
}

// 2. 创建3个星期对象
// 星期一
public static final WeekDayOld MON = new WeekDayOld();
// 星期二
public static final WeekDayOld TUE = new WeekDayOld();
// 星期三
public static final WeekDayOld WEN = new WeekDayOld();

}

枚举类型实现代码
package com.zgjy.enumDemo;
// 使用枚举类型进行星期7个对象的创建
public enum WeekDayEnum {
// 将需要创建的对象名字,写在枚举类型中即可,每个对象名字称为枚举项
/*
*// 星期一
public static final WeekDayEnum MON = new WeekDayEnum();
// 星期二
public static final WeekDayEnum TUE = new WeekDayEnum();
// 星期三
public static final WeekDayEnum WEN = new WeekDayEnum();
*
* */
Mon,TUE,WEN;
}

测试代码
package com.zgjy.enumDemo;
public class TestEnum {
public static void main(String[] args) {
WeekDayOld xq = WeekDayOld.MON;
System.out.println(xq);

	// 1. 枚举类型的第一种实现方法
	WeekDayEnum tue = WeekDayEnum.TUE;
	System.out.println(tue);// 枚举的结果是枚举项
}

}

5.3枚举类型的第二种实现方式
在枚举类型中添加一个成员变量,作用,就是用于给枚举项进行赋值
Mon----> 星期一

  1. 使用老汉式,进行对象类型的赋值,让Mon---->星期一
  2. 使用枚举实现
  1. 创建一个enum枚举类型
  2. 将可以创建的枚举对象名称写在枚举类型中
  3. 在枚举类型中添加私有的成员变量
  4. 为成员变量提供set和get方法,为了查看成员变量的值
  5. 定义私有的,有参数的构造方法,用于给成员变量赋值,但是枚举类型中的所有构造方法必须私有
  6. 枚举项后面添加小括号,表示调用对应的有参数构造方法,给成员变量赋值

老汉式实现代码
package com.zgjy.enumDemo;
// 老汉式方式实现创建7个星期对象
public class WeekDayOld {
// 3. 添加一个私有的成员变量
private String week;

public String getWeek() {
	return week;
}
public void setWeek(String week) {
	this.week = week;
}

// 1. 构造方法私有化
private WeekDayOld(String week) {
	this.week = week;
}

// 2. 创建3个星期对象
// 星期一
public static final WeekDayOld MON = new WeekDayOld("星期一");
// 星期二
public static final WeekDayOld TUE = new WeekDayOld("星期二");
// 星期三
public static final WeekDayOld WEN = new WeekDayOld("星期三");

}

枚举类型实现代码
package com.zgjy.enumDemo;
// 使用枚举类型进行星期7个对象的创建
public enum WeekDayEnum {
// 将需要创建的对象名字,写在枚举类型中即可
/*
* // 星期一
public static final WeekDayEnum MON = new WeekDayEnum(“星期一”);
// 星期二
public static final WeekDayEnum TUE = new WeekDayEnum();
// 星期三
public static final WeekDayEnum WEN = new WeekDayEnum();
*
* */
// 1. 枚举项必须写在第一行
Mon(“星期一”),TUE(“星期二”),WEN(“星期三”);
// 2. 私有的成员变量week
private String week;
// 4. 提供week的set和get方法
public String getWeek() {
return week;
}

public void setWeek(String week) {
	this.week = week;
}
// 3. 在枚举中定义构造方法,因为枚举类型中可以定义成员变量
private WeekDayEnum(String week) {
	this.week = week;
}

}

测试代码
package com.zgjy.enumDemo;
public class TestEnum {
public static void main(String[] args) {
WeekDayOld xq = WeekDayOld.MON;
System.out.println(xq);

	//2. 枚举类型添加变量的第二种实现方式
	WeekDayEnum enum1 = WeekDayEnum.TUE;
	System.out.println(enum1.getWeek());
	WeekDayEnum enum2 = WeekDayEnum.WEN;
	System.out.println(enum2.getWeek());
}

}

5.4枚举类型的第三种实现方式
在枚举类型中,可以直接添加抽象方法
枚举类型中,可以有普通方法,也可以有抽象方法
抽象方法 : 1) 存在于抽象类中 2) 存在于接口中 3) 可以存在于枚举类型中

  1. 老汉式实现方式,在类中添加一个抽象方法. 于是类就需要改变成抽象类
  2. 枚举中添加抽象方法
  1. 创建一个enum枚举类型
  2. 将可以创建的枚举对象名称写在枚举类型中
  3. 在枚举类型中添加私有的成员变量
  4. 为成员变量提供set和get方法,为了查看成员变量的值
  5. 定义私有的,有参数的构造方法,用于给成员变量赋值,但是枚举类型中的所有构造方法必须私有
  6. 枚举项后面添加小括号,表示调用对应的有参数构造方法,给成员变量赋值
  7. 在枚举中添加抽象方法
  8. 在枚举项后添加抽象方法的实现

老汉式代码
package com.zgjy.enumDemo;
// 老汉式方式实现创建7个星期对象
public abstract class WeekDayOld {
// 添加一个私有的成员变量
private String week;

public String getWeek() {
	return week;
}
public void setWeek(String week) {
	this.week = week;
}

// 1. 构造方法私有化
private WeekDayOld(String week) {
	this.week = week;
}

// 2. 创建3个星期对象
// 星期一这个对象,就需要实现抽象方法
public static final WeekDayOld MON = new WeekDayOld("星期一") {
	// 就是WeekDayOld的子类,重写抽象方法
	@Override
	public void printWeek() {
		System.out.println("很开心,今天周一");
	}
};
// 星期二
public static final WeekDayOld TUE = new WeekDayOld("星期二") {

	@Override
	public void printWeek() {
		System.out.println("很开心,今天周二");
	}
};
// 星期三
public static final WeekDayOld WEN = new WeekDayOld("星期三") {

	@Override
	public void printWeek() {
		System.out.println("很开心,今天周三");
	}
}; 

// 3. 添加一个抽象方法
public abstract void printWeek();

}

枚举类型代码
package com.zgjy.enumDemo;
// 使用枚举类型进行星期7个对象的创建
public enum WeekDayEnum {
// 将需要创建的对象名字,写在枚举类型中即可
/*
* // 星期一
public static final WeekDayEnum MON = new WeekDayEnum(“星期一”);
// 星期二
public static final WeekDayEnum TUE = new WeekDayEnum();
// 星期三
public static final WeekDayEnum WEN = new WeekDayEnum();
*
* */
// 1. 枚举项必须写在第一行
Mon(“星期一”){
@Override
public void printWeek() {
System.out.println(“枚举的星期一”);
}
},
// 枚举项需要实现枚举类型中的抽象方法
TUE(“星期二”){
@Override
public void printWeek() {
System.out.println(“枚举的星期二”);
}
},

WEN("星期三"){
	@Override
	public void printWeek() {
		System.out.println("枚举的星期三");
	}	
};
// 2. 私有的成员变量week
private String week;
// 4. 提供week的set和get方法
public String getWeek() {
	return week;
}

public void setWeek(String week) {
	this.week = week;
}
// 3. 在枚举中定义构造方法,因为枚举类型中可以定义成员变量
private WeekDayEnum(String week) {
	this.week = week;
}
// 4. 枚举类型中添加抽象方法
public abstract void printWeek();

}

5.5枚举类型定义的注意事项

  1. 枚举类型使用enum关键字,源文件.java文件,编译后的文件.class
  2. 枚举类型中可以创建的对象,都是以枚举项的方式出现,多个枚举项之间使用逗号分隔,最后一个枚举项使用;表示枚举项的结束. 枚举项必须写在枚举类型的第一行
  3. 枚举类型中可以有构造方法,但是所有的构造方法必须是私有的,如果在枚举项中没有写任何的构造方法,系统自动添加一个空参数的私有的构造
  4. 枚举类型中,可以添加抽象方法,那就需要枚举项将这个抽象方法实现

5.6枚举类型中的常用方法

  1. ordinal(): 获取枚举类型中的枚举序数,序数根据定义的枚举项,从0开始,返回值int
    类型
  2. compareTo(E o) : 比较枚举项之间的顺序大小,方法调用枚举项的序数减去参数枚举项的序数
  3. name() : 将枚举项转换成String类型
  4. values() : 将一个枚举类型中的所有枚举项获取到,返回值类型枚举类型的数组

代码
package com.zgjy.enumDemo;

public class EnumMethod {

public static void main(String[] args) {
	// 1. ordinal():枚举序数,表示定义在枚举类型中的枚举项的顺序,从0开始
	int count = WeekDayEnum.Mon.ordinal();
	System.out.println(count);
	int count1 = WeekDayEnum.TUE.ordinal();
	System.out.println(count1);
	
	// 2. compareTo(E o) : 比较枚举项之间的顺序
	// 0 - 2 = -2
	int result = WeekDayEnum.Mon.compareTo(WeekDayEnum.WEN);
	System.out.println(result);// -2 
	
	// 3.name()
	String s = WeekDayEnum.Mon.name();
	System.out.println(s);// Mon
	
	// 4. values() : 将一个枚举类型中的所有枚举项获取到,返回值类型枚举类型的数组
	WeekDayEnum[] week = WeekDayEnum.values();
	for(WeekDayEnum w : week) {
		System.out.println("----"+w);
	}
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值