零基础学习java------day18------properties集合,多线程(线程和进程,多线程的实现,线程中的方法,线程的声明周期,线程安全问题,wait/notify.notifyAll,死锁,线程池),
1.Properties集合
1.1 概述:
Properties类表示了一个持久的属性集。Properties可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串
一个属性列表可包含另一个属性列表作为它的“默认值”;如果未能在原有的属性列表中搜索到属性值,则搜索第二个属性列表
因为Properties继承于Hashtable,所以可以对Properties对象应用put和putAll方法。但不建议使用这两个方法,因为它们允许调用者插入其键或值不是String的项。相反,应该使用setProperty方法。如果在“不安全”的Properites对象(即包含非String的值或键)上调用store或save方法,则该调用将失败。类似的,如果在“不安全”的Properites对象上调用propertyNames或list方法,则该调用失败
1.2 Properites类的特点
(1)该集合不能写泛型
(2)可以持久化的属性集,键值可以存储到集合中,也可以存储到硬盘、U盘等
(3)可以和IO流有关的技术结合使用
1.3 Properites类中的一些方法
练习(来源:https://blog.****.net/gafeng123456/article/details/50846620)
1. 我有一个文本文件(user.txt),我知道数据是键值对形式的,但是不知道内容是什么。 请写一个程序判断是否有“lisi”这样的键存在,如果有就改变其实为”100” 分析: A:把文件中的数据加载到集合中 B:遍历集合,获取得到每一个键 C:判断键是否有为"小明"的,如果有就修改其值为"30岁的女人" D:把集合中的数据重新存储到文件中
Reader是抽象类,所以想获得reader需要使用其子类创建
public class PropertiesExer1 { public static void main(String[] args) { //把文件中的数据加载到集合中去 Properties pro = new Properties(); try ( FileReader fr = new FileReader("e:/b.txt"); FileWriter fw = new FileWriter("e:/b.txt"); ){ pro.load(fr); fr.close(); // 遍历集合,获取得到每一个键 Set<String> set = pro.stringPropertyNames(); for (String str : set) { if("赵雷".equals(str)) { pro.setProperty(str, "30岁的女人"); break; } } // 将集合中的数据重新存储到文件中 pro.store(fw,null); fw.close(); } catch (Exception e) { e.printStackTrace(); } } }
2. 我有一个猜数字小游戏的程序,请写一个程序实现在测试类中只能用5次,超过5次提示:游戏试玩已结束,请付费。
public class PropertiesTest2 { public static void main(String[] args) throws IOException { // 读取某个地方的数据,如果次数不大于5,可以继续玩。否则就提示"游戏试玩已结束,请付费。" // 创建一个文件 // File file = new File("count.txt"); // if (!file.exists()) { // file.createNewFile(); // } // 把数据加载到集合中 Properties prop = new Properties(); Reader r = new FileReader("count.txt"); prop.load(r); r.close(); // 我自己的程序,我当然知道里面的键是谁 String value = prop.getProperty("count"); int number = Integer.parseInt(value); if (number > 5) { System.out.println("游戏试玩已结束,请付费。"); System.exit(0); } else { number++; prop.setProperty("count", String.valueOf(number)); Writer w = new FileWriter("count.txt"); prop.store(w, null); w.close(); GuessNumber.start(); } } }
1.4 Properties配置文件的读取
目的:便于维护(如有很多类,每个类中都需要时间格式,若一个类一个类的去定义会很麻烦,若直接从Properties中的配置文件中读取就很方便,要改时间格式也只是改下配置文件中的,类只要从中读取相应的格式就行)
步骤:
(1)创建Properties对象
(2)加载配置文件
(3)使用对象获取配置文件中的信息
新建配置文件:在src右键--->new---->file---->config.properties
public class PropertiesDemo { public static void main(String[] args) { try { //1 创建一个Properties对象 Properties pro = new Properties(); //2 使用p对象,加载配置文件, 下面可可以创建一个InputStream对象传进去,但是由于配置文件的路径不好确定,所以用下面的方式比较好 pro.load(PropertiesDemo.class.getClassLoader().getResourceAsStream("config.properties")); //3 使用p对象获取配置文件中的内容 String value = pro.getProperty("date.format"); System.out.println(new SimpleDateFormat(value).format(new Date())); //获取所有的key Set<String> set = pro.stringPropertyNames(); for (String key : set) { System.out.println(key+","+pro.getProperty(key)); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2. 多线程
2.1 内容结构
2.2 线程和进程
进程:
正在运行的程序,是系统进行资源分配和调用的独立单位,每一个进程都有它自己的内存空间和系统资源
线程:
线程是进程的一个实体(执行单元或执行路径),是cpu调度和分派的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中不可少的资源(如程序计数器,一组寄存器和栈)但是它可与同属于一个进程的其他线程共享进程所拥有的的全部资源。
进程至少有一个线程,如果进程中有许多个线程----->多线程的程序
并行:某一时间点,有多个程序同时执行,多核cpu
并发:某一个时间段,有多个程序同时执行,并不是真正意义上的同时执行------>多线程
并发真的是同时执行吗?
不是,而是时间间隔很短,造成了同时执行的错觉
多线程的程序有什么优点?
提高了用户的体验,提高了程序的运行效率?----->提高了cpu的使用率
前面的代码都是单线程的程序,都是main方法中依次执行
主线程:用于执行main方法的线程
java程序运行原理:
java命令会启动java虚拟机,JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个“主线程”,然后主线程去调用某个类的main方法,所以main方法运行在主线程中
2.3 实现多线程的第一种方式
1. 继承Thread:
(1). 继承Thread
(2). 重写run方法(线程要做的事,定义在该方法中)
(3).创建子类的对象
(4).使用该对象调用start方法
Thread.currentThread:获取当前的进程
getName:获取线程的名字
用来执行main方法的线程叫做主线程,主线程的名字是main
public class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("牛郎");// 这里的对象就是线程对象 Demo d2 = new Demo("织女"); d1.start(); d2.start(); } } class Demo extends Thread{ String name; public Demo(String name) { this.name = name; } public void run() { for(int i=0;i<40;i++) { System.out.println(name+"-------"+i+Thread.currentThread().getName()); } } }
得到的打印结果是牛郎和织女错乱出现
2. 线程名字的改写
(1)setName() :改写线程的名字,如上面需要改变使用d1对象开启的线程的名字,则用d1.setName("牛郎线程"),若要改写主线程的名字,则,代码如下:
Thread.currentThread().setName("我是主线程");
(2)通过创建带有参数的构造方法实现线程名字的改写
public class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("牛郎","我是牛郎线程"); Demo d2 = new Demo("织女","我是织女线程"); d1.start(); d2.start(); } } class Demo extends Thread{ String name; public Demo(String name,String threadName) { //定义带有参数的构造方法 super(threadName); this.name = name; } public void run() { for(int i=0;i<40;i++) { System.out.println(name+"-------"+i+Thread.currentThread().getName()); } } }
3. start方法和run方法的区别
run(): 只是普通的方法调用,没有开启新的线程
start(): 开启了一个新的线程,调用run方法
run方法和main方法:
主线程的任务定义在main方法中
自定义的线程任务定义在run方法中
2.4. 多线程中常用的方法
1. 线程阻塞:
public static void steep(long millis) 静态方法,进入阻塞状态,时间结束后进入可执行状态
public class ThreadDemo1 { public static void main(String[] args) { Demo1 d1 = new Demo1(); Demo1 d2 = new Demo1(); d1.start(); d2.start(); try { Demo1.sleep(3000);// 不是让d1线程睡了3秒,而是让主线程睡得,因为它定义在主线程中 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("over"); } } class Demo1 extends Thread{ public void run() { for(int i=0;i<30;i++) { System.out.println(i+"----"+Thread.currentThread().getName()); } } }
public final void join() 成员方法,哪个线程调用它,哪个线程就先执行,执行完毕后,在执行该语句所在的线程,其只能控制两个线程
public class ThreadDemo2 { public static void main(String[] args) { Sum s = new Sum(); s.start(); try { //被哪个对象调用,就让哪个线程先执行,执行完毕后,再执行他所在的线程 s.join();//不加这句让此线程先执行的话,在sum嘉禾还没做完,主线程就先打印了这个Sum值了 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Sum.sum); } } class Sum extends Thread{ static int sum = 0; public void run() { for(int i=0;i<10000;i++) { sum += i; } } }