属性集Properties
(1)概述
是一个Map集合,可以用于存放键值对。最为主要的功能是可以与 IO 结合使用。
(2)常用方法
方法 | API | 备注 |
---|---|---|
void store(输出流对象,描述信息) | void store(OutputStream out,Stringcomments)void store(Writer writer,Stringcomments) | 存值。集合存到文件 |
void load(输入流对象) | void load(InputStreaminStream)void load(Readerreader) | 取值。文件读取到集合 |
(3)案例
//采用Properties 与IO结合的方法
public class Test04 {
public static void main(String[] args) throws IOException {
cun();
qu();
}
public static void cun() throws IOException {
String path = "JavaSEDay13\\dir\\属性集3.properties";
//存放数据
Properties pp = new Properties();
pp.setProperty("username", "张三");
pp.setProperty("password", "123");
//调用方法
FileWriter fw = new FileWriter(path);
pp.store(fw, "我第一次的修改");
fw.close();
}
public static void qu() throws IOException {
String path = "JavaSEDay13\\dir\\属性集3.properties";
//获取数据
Properties pp = new Properties();
FileReader fr = new FileReader(path);
pp.load(fr);
fr.close();
//展示结果
// pp.forEach((key, value) -> System.out.println(key + ":" + value));
Set<String> strings = prop.stringPropertyNames();
for (String key : strings) {
String value = prop.getProperty(key);
System.out.println(key + ":" + value);
}
}
}
多线程
(1)并发和并行
并行:在同一时刻,有多个指令在多个CPU上同时执行
并发:在同一时刻,有多个指令在单个CPU上交替执行
(2)进程
a) 概述:正在运行的软件
b) 特性:
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
动态性:进程的实质是程序的一次执行过程,进程是动态产生的…
并发性:…
(3)线程
a) 概述:是进程中单个顺序控制流,是一条执行路径
多线程实现方式
(1)继承Thread类
//多线程的实现方式一:继承Thread类
public class XianCheng extends Thread {
//重写 run 方法
@Override
public void run() {
//循环执行任务 1-20
for (int i = 1; i < 21; i++) {
//获取到当前执行线程的名称
String name = Thread.currentThread().getName();
System.out.println(name + "正在执行:" + i);
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
//创建对象
XianCheng xc1 = new XianCheng();
XianCheng xc2 = new XianCheng();
//设置线程的名称
xc1.setName("双神");
xc2.setName("川神");
//开启线程
xc1.start();
xc2.start();
}
}
(2)实现Runnable接口
//多线程的实现方式二:实现 Runnable 接口
public class XianCheng implements Runnable{
@Override
public void run() {
//循环执行任务 1-20
for (int i = 1; i < 21; i++) {
//获取到当前执行线程的名称
String name = Thread.currentThread().getName();
System.out.println(name + "正在执行:" + i);
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
// (1). 创建多线程实现类的对象
XianCheng xc1 = new XianCheng();
XianCheng xc2 = new XianCheng();
// (2). 将多线程实现类的对象,作为参数传递个线程类
Thread t1 = new Thread(xc1);
Thread t2 = new Thread(xc2);
// (3). 设置线程的名称
t1.setName("111");
t2.setName("222");
// (4). 开启线程
t1.start();
t2.start();
}
}
(3)实现Callable接口
//线程类,多线程实现方式三:实现Callable接口
public class XianCheng implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 21; i++) {
//获取到当前执行线程的名称
String name = Thread.currentThread().getName();
System.out.println(name+"正在执行:"+i);
}
//返回结果
return "老铁666加油!~~";
}
}
//测试类
public class Test {
public static void main(String[] args) throws Exception {
//第一个结果是第二个的参数,第二个的结果是第三个的参数
//当第三个执行完毕之后,拿到第二个的返回值
//-------------
// (1). 创建线程类的对象
XianCheng xc = new XianCheng();
// (2). 创建 FutureTask 的对象
FutureTask<String> fut = new FutureTask<>(xc);
// (3). 将上述 Fut的对象作为参数传递给线程
Thread tt = new Thread(fut);
// (4). 设置名称, 开启线程
tt.setName("老铁");
tt.start();
// (5). 获取返回值
String fanHuiZhi = fut.get();
System.out.println("fanHuiZhi = " + fanHuiZhi);
}
}
继承 Thread 类 、实现 Runnable 接口和实现Callable 接口的对比
(1)有返回值的时候(线程执行完毕之后,有结果的情况)采用Callable 接口去实现多线程
(2)如果没有返回值的时候(线程执行完毕之后,没有结果的情况),采用继承Thread 类, 或者是实现 Runnable接口(Runnable接口使用较多)
(3)继承 Thread 类。代码写法简单,但是缺点是不能继续继承其他的类
(4)实现 Runnable 接口。 代码写法复杂, 但是可以继续继承其他类,实现其他接口。
(5)后期一般会选择 实现 Runnable 接口的方式。原因: 可以继承其他类,实现其他接口,还可以采用 Lambda 表达式, 实现数据和线程的分离。
线程安全问题(购票案例)
(1)图中就出现了票数少于0的情况,这就是线程访问的安全问题(在多个线程共享一份数据,同时出现了数据的修改的时候,如果不进行处理就会出现线程安全问题)
(2)解决方案
a) 同步代码块
//线程类: 采用多线程的第二种实现方式【同步代码块解决】
public class TicketThread implements Runnable{
//定义总票数
private int ticketNum = 100;
@Override
public void run() {
//死循环不断的去卖票
while(true){
synchronized (this) { //锁对象是 this Ctrl+Alt+T
//当总票数小于等于0的时候结束
if (ticketNum<=0){
break;
}else{
// --------
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// --------
ticketNum--;
String name = Thread.currentThread().getName();
System.out.println(name+"已经卖出了票,剩余:"+ticketNum);
}
}
}
}
}
b) 同步方法
//线程类: 采用多线程的第二种实现方式【同步方法解决】
public class TicketThread implements Runnable {
//定义总票数
private int ticketNum = 100;
@Override
public void run() {
//死循环不断的去卖票 抽取方法的快捷键 Ctrl+Alt+M
while (true) {
if (sell()) break;
}
}
public synchronized boolean sell() {
//当总票数小于等于0的时候结束
if (ticketNum <= 0) {
return true;
} else {
// --------
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// --------
ticketNum--;
String name = Thread.currentThread().getName();
System.out.println(name + "已经卖出了票,剩余:" + ticketNum);
}
return false;
}
}
c) lock锁(需要采用 finally 代码块, 在 finally 代码块当中去解锁。保证一定能够解锁)
//线程类: 采用多线程的第二种实现方式【Lock锁的实现方式】
public class TicketThread implements Runnable {
//定义总票数
private int ticketNum = 100;
//[1]创建锁对象
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
//死循环不断的去卖票
while (true) {
try {
//[2]上锁
lock.lock();
//当总票数小于等于0的时候结束
if (ticketNum <= 0) {
break;
} else {
// --------
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// --------
ticketNum--;
String name = Thread.currentThread().getName();
System.out.println(name + "已经卖出了票,剩余:" + ticketNum);
}
} finally {
//[3]释放锁
lock.unlock();
}
}
}
}
死锁问题
(1)当 多个线程中出现锁的嵌套的时候就会出现死锁现象
(2)案例
//演示死锁的案例(你中有我,我中有你)
public class Test {
public static void main(String[] args) {
//准备两把锁
Object lockA = new Object();
Object lockB = new Object();
//new Thread(()->{}).start(); Lambda就是run()方法
//开启线程1
new Thread(()->{
while(true){
synchronized (lockA){
synchronized (lockB){
System.out.println("线程111,正在执行 A 中有 B");
}
}
}
}).start();
//开启线程2
new Thread(()->{
while(true){
synchronized (lockB){
synchronized (lockA){
System.out.println("线程222,正在执行 B 中有 A");
}
}
}
}).start();
}
}