线程
进程---->进行中的程序
线程---->由进程创建 一个进程可以创建多个线程
并发:同一个时刻 多个任务交替执行 造成一种貌似同时进行的错觉 简单来说 单个cpu的多任务就是并发
并行:同一个时刻 多个任务同时执行 多个cpu可以实现并行
线程的基本使用
继承Thread类
实现Runnable接口
说明 Java是单继承机制 在某些情况下 一个类可能已经继承了某个父类 这时再用继承Thread类方法 来创建线程不可能 所以可以通过实现Runnable接口来创建线程
常用方法第二组
yield 线程的礼让 不一定成功
join 线程的插队 插队的线程一旦插队成功 则肯定先执行完插入的线程所有的任务
用户线程 也叫工作线程 当线程的任务执行完 或 通知方式结束
守护线程 一般是为工作线程服务的 当所有的用户线程结束 守护线程自动结束
常见的守护线程:垃圾回收机制
线程的生命周期
NEW 尚未启动的线程处于该状态
RUNNABLE 在Java虚拟机中执行的线程处于此状态 (READY RUNNING)
BLOCKED 在阻塞等待监视器锁定的线程处于此状态
WAITING 正在等待另一个线程执行特定动作的线程处于此状态
TIMED_WAITING正在等待另一个线程执行动作达到指定等待时间的线程处于该状态
TERMINATED 已退出的线程处于此状态
线程的同步
在多线程编程中 一些敏感数据不允许被多个线程同时访问 此时就使用同步访问计数 保证数据在任何同一时刻 最多有一个线程访问 以保证数据的完整性
理解 线程同步 即当有一个线程在对内存进行操作时 其他线程都不可以对这个内存地址进行操作 知道该线程完成操作 其他线程才能对该内存地址进行操作
同步具体方法
1. 同步代码块
synchronized(对象) {
得到对象的锁 才能操作同步代码
}
2.同步方法
public synchronized void m(String name) {
}
互斥锁
介绍
- Java中 引入对象互斥锁的概念 来保证共享数据操作的完整性
- 每个对象都对应了一个可称为"互斥锁"的标记 这个标记用来保证在任一时刻 只能有一个线程访问该对象
- 关键字synchronized来与对象的互斥锁联系 当某个对象用synchronized修饰时 表明该对象在任一时刻只能由一个线程访问
- 同步的局限性 导致程序的执行效率要降低
- 同步方法(非静态的)的锁可以是this 也可以是其他对象
- 同步方法(静态的) 的锁为当前类本身
同步方法如果没有使用static修饰 默认锁对象为this
如果方法使用static修饰 默认锁对象 当前类.class
线程的死锁
多个线程都占用了对方的锁资源 不肯礼让 导致了死锁
模拟线程死锁
点击查看代码
public class DeadLock_ {
public static void main(String[] args) {
DeadLockDemo A = new DeadLockDemo(true);
A.setName("A 线程");
DeadLockDemo B = new DeadLockDemo(false);
B.setName("B 线程");
A.start();
B.start();
}
}
@SuppressWarnings({"all"})
class DeadLockDemo extends Thread {
static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {//构造器
this.flag = flag;
}
@Override
public void run() {
//下面业务逻辑的分析
//1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程 A 得不到 o2 对象锁,就会 Blocked
//3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁
//4. 如果线程 B 得不到 o1 对象锁,就会 Blocked
if (flag) {
synchronized (o1) {//对象互斥锁, 下面就是同步代码
System.out.println(Thread.currentThread().getName() + " 进入 1");
synchronized (o2) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 2");
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 进入 3");
synchronized (o1) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 4");
}
}
}
}
释放锁
下面操作会释放锁
1.当前线程的同步方法 同步代码块执行结束
2.当前线程在同步代码块 同步方法中遇到break return
3.当前线程在同步代码块 同步方法中出现了未处理的Error 或 Exception 导致异常结束
4.当前线程在同步代码块 同步方法中执行了 线程对象的wait方法 当前线程暂停 并释放锁
下面操作不会释放锁
1.线程执行同步代码块或同步方法时 程序调用Thread.sleep() Thread.yield()方法 暂停当前线程的执行 不会释放锁
2.线程执行同步代码块时 其他线程调用了该线程的suspend()方法 将该线程挂起 该线程不会释放锁
方法不再推荐使用
IO流
文件
文件在程序中是以流的方式进行操作的
File类 最基本的文件的相关类
`
//方式1 new File(String pathname) //根据路径构建一个File对象
@Test
public void create01() {
String fliePath = "C:\let's get it\JAVA\chapter16\news.txt";
File file = new File(fliePath);
try {
file.createNewFile();//创建文件方法
System.out.println("success");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式2 new File(File parent, String child) //根据父目录文件+子路径构建
//
@Test
public void create02() {
File parentfile = new File("C:\let's get it\JAVA\chapter16");
String fileName = "news1.txt";
//这里的file对象 在Java程序中 只是一个对象
//只有执行了 createNewFile 方法 才会真正地在磁盘创建该文件
File file = new File(parentfile, fileName);
try {
file.createNewFile();
System.out.println("success");
} catch (IOException e) {
e.printStackTrace();
}
}
//方式3 new File(String parent , String child)//根据父目录+子路径构建
@Test
public void create03() {
String parantPath = "C:\let's get it\JAVA\chapter16";
String fileName = "new2.txt";
File file = new File(parantPath , fileName);
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
`
最基本的文件相关方法
点击查看代码
//调用相应的方法 得到对应信息
System.out.println("文件名 = " + file.getName());
//绝对路径
System.out.println("文件绝对路径 = " + file.getAbsolutePath());
//父级目录
System.out.println("文件父级目录 = " + file.getParent());
//文件大小
System.out.println("文件大小 = " + file.length());
//文件是否存在
System.out.println("文件是否存在 = " + file.exists());
//是不是一个文件
System.out.println("是不是一个文件 = " + file.isFile());
//是不是一个目录
System.out.println("是不是一个目录 = " + file.isDirectory());
节点流 处理流
节点流 可以从一个特定的数据源读写数据
处理流(也叫包装流) 是 连接在已存在的流 (节点流或者处理流之上) 为程序提供更为强大的读写功能 也更加灵活
处理流(包装流)包装节点流 既可以消除不同节点流的实现差异 也可以提供更方便的方法来完成输入输出
处理流对节点流包装 使用了修饰器的设计模式 不会与数据源直接相连
节点流 是低级流/底层流 直接跟数据源相连
FileInputStream
String filePath = "xx\xx\xx";
int readData = 0;
FileInputStream fileInputStream = null;
try {
//创建 FileInputStream 对象 用于读取文件
fileInputStream = new FileInputStream(filePath);
//从该输入流读取一个字节的数据 如果没有输入可用 此方法将停止
//如果返回-1 表示读取完毕
while ((readData = fileInputStream.read()) != -1) {
System.out.print((char)readData);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭文件流 释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
FileOutputStream
//创建 FileOutputSteam对象
String filePath = "xx\xx\xx";
FileOutputStream fileOutputStream = null;
//new FileOutputStream(filePath) 创建方式 当写入内容 会覆盖原来的内容
//new FileOutputStream(filePath , true) 创建方式 当写入内容 会追加到文件的后面
try {
// 得到 fileOutputStream 对象
fileOutputStream = new FileOutputStream(filePath, true);
//写入一个字节
// fileOutputStream.write('z');
//
String str = "hello world";
// str.getBytes 可以把字符串 --- 字节数组
fileOutputStream.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭文件流 释放资源
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
FileReader
tring filePath = "xx\xx\xx";
FileReader fileReader = null;
int data = ' ';
// 1. 创建FileReader对象
try {
fileReader = new FileReader(filePath);
//循环读取 使用read
while((data = fileReader.read()) != -1) {
System.out.print((char)data);
}
} catch (IOException e) {
e.printStackTrace();
}
FileWriter
String filePath = "xx\xx";
//创建FileWriter对象
FileWriter fileWriter = null;
char chars[] = {'a', 'b', 'c'};
try {
fileWriter = new FileWriter(filePath);//默认是覆盖写入
//write(int):写入单个字符
fileWriter.write('H');
//write(char[]): 写入指定数组
fileWriter.write(chars);
//write(char[] , off , len):写入指定数组的指定部分
fileWriter.write("哈哈哈哈哈".toCharArray(), 0, 3);
//write(string):写入整个字符串
fileWriter.write(" 你好哈哈哈哈");
//write(string,off,len):写入字符串的指定部分
fileWriter.write("上海天津",0,2);
//在数据量大的情况下 可以使用循环操作
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//对于FileWriter 一定要关闭流 或者flush才能真正的把数据写入到文件
/*
*/
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
处理流
处理流的好处
性能的提高:主要以增加缓冲的方式来提高输入输出的效率
操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据使用更加灵活方便
BufferedReader 字符流
String filePath = "C:\\Users\\19665\\Desktop\\Java相关\\caiquan.java";
//创建bufferedReader
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)); FileReader是访问文件方法的基类节点流
String line; //按行读取 效率高
while ((line = bufferedReader.readLine()) != null) {
//按行读取文件 返回null时 表示文件读取完毕
System.out.println(line);
}
//关闭流 这里注意 只需要关闭BufferedReader 底层会自动关闭节点流
bufferedReader.close();
/* 源代码
public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
try {
in.close(); in就是我们传入的fileReader
} finally {
in = null;
cb = null;
}
}
}
*/
BufferedWriter 字符流
String filePath = "C:\\Users\\19665\\Desktop\\Java相关\\ok.txt";
//new FileWriter(filePath,true)表示以追加的方式写入
//new FileWriter(filePath) 表示以覆盖的方式写入
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath)); FileWriter是访问文件的抽象基类 节点流
bufferedWriter.write("hhh");
bufferedWriter.newLine();//插入一个换行
bufferedWriter.write("www");
bufferedWriter.newLine();//插入一个换行
bufferedWriter.write("zzz");
bufferedWriter.newLine();//插入一个换行
bufferedWriter.close();
BufferedInputStream 字节流
BufferedOutputStream 字节流
对象流
序列化 反序列化
序列化就是保存数据 保存数据的值和数据类型
反序列化就是恢复数据 恢复数据的值和数据类型
需要让某个对象支持序列化机制 则必须让其类是可序列化的 要让某个类可序列化 该类必须实现 Serializable
ObjectOutputStream 提供序列化功能
//序列化后 保存的文件格式 不是纯文本 而是按照他的格式来保存
String filePath = "xx\\xx";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
//序列化数据到 ok1.dat
oos.writeInt(100); // int --> Integer(implement Serializable)
oos.writeBoolean(true);;// boolean -> Boolean (实现了 Serializable)
oos.writeChar('a');// char -> Character (实现了 Serializable)
oos.writeDouble(9.5);// double -> Double (实现了 Serializable)
oos.writeUTF("嘿嘿");//String
//保存一个 dog 对象
oos.writeObject(new Dog("旺财", 10, "日本", "白色"));
oos.close();
System.out.println("数据保存完毕(序列化形式)");
ObjectInputstream 提供反序列化功能
String filePath = "xx\\xx";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
Object dog = ois.readObject();
System.out.println("运行类型 = " + dog.getClass());
System.out.println("dog信息 = " + dog);
//重要细节
//1.如果我们希望调用Dog的方法 需要向下转型
//2.需要将Dog类的定义拷贝到可以引用的位置
Dog dog2 = (Dog)dog;
System.out.println(dog2.name);
ois.close();
注意事项
- 读写顺序要一致
- 要求序列化或反序列化对象 需要实现Serializable
- 序列化的类种建议添加SerialVersionUID 为了提高版本的兼容性
- 序列化对象时 默认将里面所有属性都进行序列化 但除了static 或 transient 修饰的成员
- 序列化对象时 要求里面属性的类型也需要实现序列化接口
- 序列化具有可继承性 如果某类已经实现可序列化 则他的子类默认实现可序列化
标准输入输出流
// System 类的 public final static InputStream in = null;
// System.in 编译类型 InputStream
// System.in 运行类型 BufferedInputStream
// 表示标准输入 键盘
System.out.println(System.in.getClass());
// System.out public final static PrintStream out = null;
// 编译类型 PrintStream
// 运行类型 PrintStream
// 表示标准输出 显示器
//System.out.println(System.out.getClass());
转换流
InputStreamReader
String filePath = "\\xx\\xx";
// FileInputStream 转成 InputStreamReader
// 指定编码 gbk
// InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
// InputStreamReader 传入 BufferedReader
// BufferedReader br = new BufferedReader(isr);
//合并
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "gbk"));
// 读取
String s = br.readLine();
System.out.println("读取内容 " + s);
br.close();
OutputStreamWriter
String filePath = "\\xx\\xx";
// FileInputStream 转成 InputStreamReader
// 指定编码 gbk
//InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
// InputStreamReader 传入 BufferedReader
//BufferedReader br = new BufferedReader(isr);
//合并
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "gbk"));
// 读取
String s = br.readLine();
System.out.println("读取内容 " + s);
br.close();
Properties