IO 流
概述
1、IO流用来处理设备之间的数据传输
上传文件和下载文件
2、Java对数据的操作是通过流的方式
3、Java用于操作流的对象都在IO包中I input 从硬盘读取数据到内存 read 读 O output 从内存写入数据到硬盘 write 写
分类
按照数据流向
输入流 读入数据 字节输入流、字符输入流
输出流 写出数据 字节输出流、字符输出流
按照数据类型
字节流 字节输入流、字节输出流
字符流 字符输入流、字符输出流
class InputStream 字节输入流基类
--| class FileInputStream 文件操作字节输入流
class OutputStream 字节输出流基类
--| class FileOutputStream 文件操作字节输出流
class Reader 字符输入流基类
--| class FileReader 文件操作字符输入流
class Writer 字符输出流基类
--| class FileWriter 文件操作字符输出流
缓冲流:
BufferedInputStream
字节输入缓冲流
BufferedOutputStream
字节输出缓冲流
BufferedReader
字符输入缓冲流
BufferedWriter
字符输出缓冲流
FileInputStream 文件操作字节输入流
Constructor构造方法
// 根据用户指定的文件路径创建对应的FileInputStream,文件操作输入字节流,如果文件不存在,抛出异常FileNotFoundException
FileInputStream(String filePath);
// 根据用户指定对应文件的File类对象,创建对应的FileInputStream,如果文件不存在,抛出异常FileNotFoundException
FileInputStream(File file);
Method成员方法
// 从文件中读取一个字节数据返回。如果读取到底末尾,返回-1 EOF End Of File
int read();
// 从文件中读取数据到缓冲数组buf中,返回值类型是从文件中读取到的字节个数,如果读取到文件末尾,返回-1, EOF End Of File 。如果在运行过程中出现了问题,抛出异常IOException
int read(byte[] buf); 【重点,效率高】
操作流程
1. 明确对应文件的路径,可以选择直接给予对应的String类型路径,或者创建对应的File类对象,作为参数
2. 创建FileInputStream文件操作字节输入流对象,打开文件操作管道
3. 从FileInputStream对象中使用方法,读取数据
4. 关闭资源!!!FileInputStream类对象 ==> 水龙头!!!
案例代码
public class Test1 {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream(new File("C:/Users/CJF/Desktop/Test.txt"));
int content = fileInputStream.read();
System.out.println((char) content);
// byte[] buf = new byte[1024 * 16];
while (-1 != (content = fileInputStream.read())) {
System.out.print((char)content);
}
fileInputStream.close();
}
}
案例代码二
public class Test2 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream(new File("C:/Users/CJF/Desktop/Test.txt"));
byte[] buf = new byte[1024 * 16];
int count = -1;
while (-1 != (count = fis.read(buf))) {
System.out.println(new String(buf, 0, count));
}
fis.close();
}
}
【注意】一般都会使用缓冲数组,因为单个字节读取效率太低了
FileOutputStream 文件操作字节输出流
Constructor构造方法
// 根据用户指定的路径,创建对应的FileOutputStream文件操作输出流对象。如果路径不合法,抛出异常FileNotFoundException。采用写入数据到文件的方式,是【删除写】!!!文件内容清空,再写入数据
FileOutputStream(String filePath);
// 根据用户指定的File类对象,创建对应FileOutputStream文件操作输出流对象,如果路径不合法,抛出异常FileNotFoundException。采用写入数据到文件的方式,是【删除写】!!!文件内容清空,再写入数据
FileOutputStream(File file);
// 根据用户指定的路径,创建对应的FileOutputStream文件操作输出流对象。如果路径不合法,抛出异常FileNotFoundException。
// append参数是boolean类型,如果传入参数为true,表示【追加写】,在文件末尾写入数据
FileOutputStream(String filePath, boolean append);
// 根据用户指定的File类对象,创建对应FileOutputStream文件操作输出流对象,如果路径不合法,抛出异常FileNotFoundException。
// append参数是boolean类型,如果传入参数为true,表示【追加写】,在文件末尾写入数据
FileOutputStream(File file, boolean append);
Method成员方法
// 写入一个字节数据写入到文件中
void write(int b);
// 写入一个字节数组到文件中
void write(byte[] buf);
// 写入一个字节数组到文件中,要求从off偏移位置开始,计数count
void write(byte[] buf, int off, int count);
操作流程
1. 明确对应文件的路径,可以选择直接给予对应的String类型路径,或者创建对应的File类对象,作为参数
2. 创建FileOutputStream文件操作输出字节流对象,打开文件操作管道
3. 使用FileOutputStream对象写入数据到文件中
4. 关闭资源!!!
【注意】
1. FileOutputStream拥有创建文件的能力,在路径合法,且对应目录有写入权限下可以创建文件
2. 区分删除写和追加写
案例代码
public class Test {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream(new File("C:/Users/CJF/Desktop/Test.txt"));
fos.write(97);
System.out.println();
FileOutputStream fos2 = new FileOutputStream(new File("C:/Users/CJF/Desktop/Test.txt"), true);
fos2.write("\n今天是个好天气".getBytes());
fos2.close();
fos.close();
}
}
FileReader 文件操作字符输入流
Constructor构造方法
// 根据指定路径的文件创建对应的文件字符输入流对象,如果文件不存在,抛出异常FileNotFoundException
FileReader(String filePath);
// 根据指定路径的File类对象创建文件字符输入流对象,如果文件不存在,抛出异常FileNotFoundException
FileReader(File file);
Method成员方法
// 从文件中读取一个字符数据,返回值为int类型,int类型数据中有且只有低十六位是有效数据,如果读取到文件末尾返回-1 EOF End Of File
int read();
// 从文件中读取数据到char类型缓冲数组buf,返回值是读取到字符个数。如果读取到文件末尾返回-1 EOF End Of File
int read(char[] buf);
操作流程
1. 明确需要读取数据的文件
2. 创建FileReader对象,打开文件操作管道
3. 使用FileReader类对象方法,读取文件数据
4. 关闭资源
案例代码
public class Test {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader(new File("C:/Users/CJF/Desktop/Test.txt"));
char[] buf = new char[1024 * 16];
int content = -1;
while (-1 != (content = fr.read(buf))) {
System.out.println(new String(buf, 0, content));
}
fr.close();
}
}
FileWriter 文件操作字符输出流
Constructor构造方法
// 根据用户指定的路径,创建对应的FileWriter文件操作字符输出流对象。如果路径不合法,抛出异常FileNotFoundException。采用写入数据到文件的方式,是【删除写】!!!文件内容清空,在写入数据
FileWriter( String filePath);
// 根据用户指定的File类对象,创建对应FileWriter文件操作字符输出流对象,如果路径不合法,抛出异常FileNotFoundException。采用写入数据到文件的方式,是【删除写】!!!文件内容清空,在写入数据
FileWriter(File file);
// 根据用户指定的路径,创建对应的FileWriter文件操作字符输出流对象。如果路径不合法,抛出异常FileNotFoundException。append参数是boolean类型,如果传入参数为true,表示【追加写】,在文件末尾写入数据
FileWriter(String filePath, boolean append);
// 根据用户指定的File类对象,创建对应FileWriter文件操作字符输出流对象,如果路径不合法,抛出异常FileNotFoundException。append参数是boolean类型,如果传入参数为true,表示【追加写】,在文件末尾写入数据
FileWriter(File file, boolean append);
Method成员方法
// 写入一个字符数据写入到文件中
void write(int ch);
// 写入一个字符数组到文件中
void write(char[] buf);
// 写入一个字符数组到文件中,要求从off偏移位置开始,计数count
void write(char[] buf, int off, int count);
// 写入一个字符串到文件中
void write(String str);
// 写入一个字符串到文件中,要求从offset偏移位置开始,计数count
void write(String str, int offset, int count);
【注意】
1. FileWriter拥有创建文件的能力,在路径合法,且对应目录有写入权限下可以创建文件
2. 区分删除写和追加写
案例代码
public class Test {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter(new File("C:/Users/CJF/Desktop/Test.txt"), true);
fw.write("\n今天是520情人节");
fw.close();
}
}
复制文件
public class Demo8 {
public static void main(String[] args) throws IOException {
// 明确操作源文件
File src = new File("D:/EclipseWorkSpace/FC2020/src/com/fc/z/io/a.txt");
// 明确操作源文件
File dest = new File("D:/EclipseWorkSpace/FC2020/src/com/fc/z/io/b.txt");
// 创建读对象
FileInputStream fis = new FileInputStream(src);
// 创建写对象
FileOutputStream fos = new FileOutputStream(dest);
// 缓存
byte[] buf = new byte[1024 * 16];
int content = -1;
// 读取数据并写入
while (-1 != (content = fis.read(buf))) {
fos.write(buf, 0, content);
}
// 关闭资源
fos.close();
fis.close();
}
}
总结
1、流程是一样的 明确文件 打开管道 操作文件 关闭资源 2、核心方法 read 读取,输入 write 写入,输出 3、输出流有创建文件的能力。 4、 输出流需要注意是删除写还是追加写。 5、输入流有缓冲比没有缓冲效率高很多 6、一定要注意关闭资源!!!resource 7、一般还是用字节流,避免文件损坏
缓冲流
概述
1、缓冲流是Java中提供的系统缓冲,底层都是一个缓冲数组,根据处理的数据方式不同,提供的数据有字节缓冲数组和字符缓冲数组。 2、字节缓冲流,默认的字节数组缓冲区是8KB byte[] buffer = new byte[1024 * 8]; 3、字符缓冲流,默认的字符数组缓冲区是16KB char[] buffer = new char[1024 * 8]; 4、【重点】 任何一个缓冲流都没有任何操作文件的能力!!!读取文件数据,写入文件数据,都是依赖于对应的字符流或者字节流提供的!!! 5、缓冲流使用的方法,也是read write 而且是对应创建当前缓冲流使用的字符流或者字节流的!!! 6、缓冲流减少了CPU通过内存访问磁盘或者说文件的次数。极大的提高了开发效率。IO流操作文件内容都是在缓冲流内部的缓冲数组中,也就是内存中。
分类
BufferedInputStream 字节缓冲输入流 BufferedOutputStream 字节缓冲输出流 BufferedReader 字符缓冲输入流 BufferedWriter 字符缓冲输出流
BufferedInputStream 字节缓冲输入流
构造方法 Constructor
BufferedInputStream(InputStream in);
这里需要的参数是字节输入流对象
成员方法 Method
int read();
int read(byte[] buf);
其实就是InputStream中使用的方法
案例代码
public class Test {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(new File("C:/Users/CJF/Desktop/Test.txt")));
byte[] buf = new byte[1024 * 8];
int count = -1;
while (-1 != (count = bis.read(buf))) {
System.out.println(new String(buf, 0, count));
}
bis.close();
}
}
BufferedOutputStream 字节缓冲输出流
构造方法 Constructor
BufferedOutputStream(OutputStream out);
这里需要一个字节输出流作为方法的参数
常用方法 Method
void write(int b);
void write(byte[] buf);
void write(byte[] buf, int off, int len);
以上方法都是OutputStream提供的方法。
所有的数据都是首先都是写入保存到BufferedOutputStream 底层操作的数组中,当数组填满以后,或者执行指定的方法,才会将数据之间写入到内存中。
案例代码
public class Test {
public static void main(String[] args) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(new File("C:/Users/CJF/Desktop/Test.txt"), true));
bos.write("\n今天是个好天气".getBytes());
bos.close();
}
}
效率总结
1、使用缓冲时间效率是远远高于未使用缓冲情况,这里是一个非常经典的空间换时间概念
缓冲占用内存 16KB 非缓冲 4byte 时间效率大于250倍 空间占用4000倍
2、利用代码可以发现,非缓冲IO操作时使用数组作为缓冲区和使用缓冲流操作,时间效率相似。这里还是推荐使用系统提供的缓冲流,更加安全,并且提供了一些其他方法,可以作为一定参考和使用。
BufferedReader 字符输入缓冲流
构造方法 Constructor
BufferedReader(Reader in);
常用方法 Method
int read();
int read(byte[] buf);
String readLine(); 【新方法】
从文件中读取一行数据,返回值类型是字符串,如果读取到文件默认,返回null
一行数据??? 结尾标记 \r\n
案例代码
public class Test {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(new File("C:/Users/CJF/Desktop/Test.txt")));
char[] buf = new char[1024 * 8];
int count = -1;
while (-1 != (count = br.read(buf))) {
System.out.println(new String(buf, 0, count));
}
String content = null;
while (null != (content = br.readLine())) {
System.out.println(content); // 这里不会再输出,因为上面已经读到文件末尾,即 null == content
}
br.close();
}
}
BufferedWriter 字符输出缓冲流
构造方法 Constructor
BufferedWriter(Writer in);
常用方法
void write(int ch);
void write(char[] buf);
void write(char[] buf, int off, int len);
void write(String str);
void write(String str, int off, int len);
void newLine();
换行写操作
案例代码
public class Test {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("C:/Users/CJF/Desktop/Test.txt"), true));
bw.write("\n今天又是个好天气");
bw.newLine();
bw.close();
}
}
多线程
进程和线程
进程
概述:
正在运行的程序,是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
例如:
打开任务管理器,可以看到电脑中执行的每一个程序,每一个程序就是一个进程
特点:
1、独立性
2、动态性
3、并发性
线程
概述:
是进程中的单个顺序控制流,是一条执行路径
一个进程如果只有一条执行路径,则称为单线程程序
一个进程如果有多条执行路径,则称为多线程程序
例如:
电脑管家就是一个程序 => 进程
电脑管家可以同时 病毒查杀,垃圾清理,一键加速等功能
这些每个功能都可以看做是线程,它们是同时进行的
特点:
1、线程是CPU的最小调度单位,CPU可以很快的在多个线程间实现切换。
2、运行时的线程,随时都可以被CPU给挂起。
3、线程的抢占发生在任意时期
进程与线程的区别
1、一个进程可以有多个线程。但是必须要有一个主线程
2、进程间不能共享资源,但是线程间可以共享资源。
3、Java程序中,最少要有两个线程
1、main线程
2、JVM 的 GC 机制,守护线程
并发和并行
并发:两个或者两个以上的事务在同一个时间段发生
并行:两个或者两个以上的事务在同一个时刻发生
线程的使用
Thread类
Java中的一个线程类,同时提供了很多线程的操作使用的方法,我们想要操作线程,必须通过Thread类对象去完成。Thread 类实现了 Runnable 接口,其中的 run 方法中就是我们要被运行的代码。可以通过重写 run 方法,并调用线程的 start 使其运行,以达到我们想要的效果
自定义线程三种方式
1、自定义线程类,继承自 Thread,并重写 run 方法
2、自定义线程类,传入一个实现了 Runnable 接口的参数【重点】
3、自定义线程类,传入一个实现了 Callable 接口的参数
继承 Thread 类并重写 run 方法
案例代码
public class Demo1 {
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
thread1.start();
System.out.println("main线程");
}
}
class Thread1 extends Thread {
@Override
public void run() {
System.out.println("测试线程");
}
}
实现 Runnable 接口
public class Demo2 {
public static void main(String[] args) {
Thread thread = new Thread(new Thread2());
thread.start();
}
}
class Thread2 implements Runnable {
@Override
public void run() {
System.out.println("测试线程");
}
}
或
public class Demo2 {
public static void main(String[] args) {
// 使用匿名内部类和匿名对象
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("测试线程");
}
}).start();
}
}
实现 Callable 接口
可以有返回值
可以抛出异常
public class Test3 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Integer> cal = new Callable<Integer>() {
int count = 0;
@Override
public Integer call() throws Exception {
count++;
System.out.println(" Test :" + count); // Test :1
return count;
}
};
FutureTask<Integer> future = new FutureTask<Integer>(cal);
Thread thread = new Thread(future);
thread.start();
System.out.println("返回值为:" + future.get()); // 返回值为:1
}
}
三种创建方式的区别
继承 Thread 类:
编写简单、单继承,所以这种类无法再继承其他类、无法实现多个线程的资源共享、扩展性无
实现 Runnable 接口:
编写复杂一点,接口可以多实现,可以实现多个线程的资源共享 推荐使用
实现 Callable 接口:
编码复杂,可以实现线程执行完之后进行值的返回
【重点】开发中,需要线程返回值,就使用 Callable,不需要返回值的就可以 Runnable ,最常用的是实现 Runnable
线程调度
分时调度模型
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
抢占式调度模型
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
Java 采用的是抢占式调度模型
优先级
线程的优先级就是线程获得CPU的概率,优先级越高,获取CPU的概率越大
Java中共有10种优先级,从小到大,1-10之间。10是优先级最高,默认的优先级是5
优先级方法
【注意】即便我们设置了优先级,也只是增加抢占的概率,线程并不一定会严格按照优先级执行
// 获取当前线程的优先级
public final int getPriority()
// 设置当前线程的优先级
public final void setPriority(int newPriority)
案例代码
public class Demo4 {
public static void main(String[] args) {
// 创建两个Runnable接口实现类
Runnable run1 = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("烤羊排");
}
}
};
Runnable run2 = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("大盘鸡");
}
}
};
// 创建两个线程
Thread thread1 = new Thread(run1);
Thread thread2 = new Thread(run2);
// 查看优先级
System.out.println(thread1.getPriority());
System.out.println(thread2.getPriority());
// 设置优先级
thread1.setPriority(10);
thread2.setPriority(1);
thread2.start();
thread1.start();
// 主线程
for (int i = 0; i < 10; i++) {
System.out.println("main线程");
}
}
}
【注意】设置优先级需要在 start 方法之前设置
线程状态(生命周期)
简单理解生命周期
线程有五大状态,分别是新建、就绪、运行、阻塞、销毁
新建:
当我们实例化线程对象的时候,线程就是新建状态
就绪:
当我们调用线程的start方法之后,线程就会进入就绪状态
处于该状态的线程,随时都可以获取CPU调度
运行:
线程获取CPU的调度之后,线程抢到了时间片,可以用来运行自己任务
阻塞:
当线程因为资源竞争,或主动方法调用,让线程进入到阻塞。
常见:sleep、wait、join等等
销毁:
当线程的run方法执行结束之后,就会进入到销毁状态
【注意】程序的结束就是指的内部的所有线程全部进入到了销毁状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngcoVYo9-1659328213515)(Pictures/线程的生命周期.jpg)]
扩展:六种线程状态
如果按照 java.lang.Thread.State 枚举方式,一共提供了6种线程状态
状态 | 导致状态的发生条件 |
---|---|
NEW(新建) | 线程刚刚被创建,没有启动,没有调用start方法 |
RUNNABLE(可运行) | 线程已经可以在JVM中运行,但是是否运行不确定,看当前线程是否拥有CPU执行权 |
BLOCKED(锁阻塞) | 当前线程进入一个同步代码需要获取对应的锁对象,但是发现当前锁对象被其他线程持有,当前线程会进入一个BLOCKED。如果占用锁对象的线程打开锁对象,当前线程持有对应锁对象,进入Runnable状态 |
WAITING(无限等待) | 通过一个wait方法线程进入一个无限等待状态,这里需要另外一个线程进行唤醒操作。进入无限等待状态的线程是无法自己回到Runnable状态,需要其他线程通过notify或者notifyAll方法进行唤醒操作 |
TIMED_WAITING(计时等待) | 当前线程的确是等待状态,但是会在一定时间之后自动回到Runnable状态,例如 Thread.sleep() 或者是Object类内的wait(int ms); |
TERMINATED(被终止) | 因为Run方法运行结束正常退出线程,或者说在运行的过程中因为出现异常导致当前线程被销毁 |
TIMED_WAITING(计时等待)
// 在对应线程代码块中,当前线程休眠指定的时间
Thread.sleep(int ms);
sleep方法
1. 调用之后休眠指定时间
2. sleep方法必须执行在run方法内,才可以休眠线程
3. sleep不会打卡当前线程占用的锁对象。
// 让当前线程进入一个计时等待状态
void wait(long timeout);
Object类内
1. 规定的时间及时完毕,线程回到可运行状态
2. 在等待时间内,通过其他线程被notify或者notifyAll唤醒
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jrwcq2ll-1659328213516)(Pictures/TIMED_WAITING状态.png)]
BLOCKED(锁阻塞)
线程中有锁存在,线程需要进入带有锁操作的同步代码,如果锁对象被别人持有,只能在锁外等待
锁阻塞状态的线程是否能够抢到锁对象有很多因素
1. 优先级问题,非决定因素
2. CPU执行概率问题。
后期高并发一定会存在多线程操作锁对象问题,秒杀,抢购...
队列方式来处理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z6gjeM8P-1659328213517)(Pictures/BLOCKED锁阻塞状态.png)]
线程状态 WAITING(无限等待)
当某一个线程被执行wait()方法,需要等待另外的一个线程进行唤醒操作。
public void wait();
在哪一个线程中执行,就会让当前线程进入一个无限等待状态。
1. 所在线程进入无限等待状态
2. 开启【锁对象】
public void notify();
唤醒和当前锁对象有关的无限等待线程中的一个,随机选择。
1. 唤醒一个无限等待状态线程
2. 开启【锁对象】
public void notifyAll();
唤醒所有和当前锁对象有关的无限等待线程
1. 唤醒所有线程
2. 开启【锁对象】
3. 线程进入锁对象抢占过程,就有可能进入一个锁阻塞状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2i5MhV0k-1659328213517)(Pictures/无限等待图例.png)]
线程执行的所有状态分析图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zx2VrWee-1659328213518)(Pictures/六大线程状态.png)]
守护线程
线程分为:用户线程和守护线程,Java中默认创建的线程就是用户线程
特点
当守护的用户线程销毁的时候,守护线程也会跟着消亡。无论守护线程是否执行结束都会随着用户线程一起销毁
作用
1、自动下载
2、操作日志
3、操作监控
方法
// 判断该线程是否为守护线程
boolean isDaemon();
// 当传入 true 时,将当前线程设置为守护线程:
void setDaemon(boolean on)
案例代码
public class Demo5 {
public static void main(String[] args) {
// 创建两个线程
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("大盘鸡");
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("烤羊排" + i);
}
}
});
// 查看是否是守护线程
System.out.println(thread2.isDaemon());
// 设置守护线程
thread2.setDaemon(true);
thread1.start();
thread2.start();
// 主线程
for (int i = 0; i < 10; i++) {
System.out.println("main线程");
}
}
}
【注意】主线程和 GC(**garbage collection **垃圾回收机制) 线程就是一对用户线程与守护线程,GC 守护主线程
线程控制
方法
// 线程休眠(运行-->阻塞):
static void sleep(long millis);
// 线程加入(运行-->阻塞):
void join();
// 线程礼让(运行-->就绪):
void yield();
sleep()
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
public class Demo6 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("大盘鸡" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
// 主线程
for (int i = 0; i < 10; i++) {
System.out.println("main线程");
}
}
}
【注意】使用sleep方法需要对其进行异常捕获
join()
在当前线程中,执行另一个线程的join方法,然后当前线程就会阻塞,等待插入的线程执行完毕之后,才会从阻塞状态进入到就绪状态,重新参与CPU抢夺!
就绪状态的线程的抢占发生在任意时期
public class Demo7 {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("烤羊排" + i);
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("大盘鸡" + i);
}
}
});
thread2.start();
// 线程加入
thread2.join();
thread1.start();
// 主线程
for (int i = 0; i < 10; i++) {
System.out.println("main线程");
}
}
}
yidle()
可以让当前正在运行的线程暂停,并执行其他线程。但是不会让当前线程阻塞,而且让当前的线程进入到就绪状态。
实际上:线程执行yield之后,只有比当前线程的优先级更高或者相同的才有机会参与抢夺CPU,而且当前线程也会参与抢夺
public class Demo8 {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("烤羊排" + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("大盘鸡" + i);
if (i == 5) {
System.out.println("线程礼让");
Thread.yield();
}
}
}
});
thread1.setPriority(9);
thread2.setPriority(1);
thread2.start();
thread1.start();
// 主线程
System.out.println("main线程");
}
}
线程相关方法
static Thread currentThread();
返回对当前正在执行的线程对象的引用
long getId();
返回该线程的标识符。线程 ID 是一个正的 long 数,在创建该线程时生成。线程 ID 是唯一的,并终生不变。线程终止时,该线程 ID 可以被重新使用
String getName();
返回该线程的名称
void setName(String name);
改变线程名称,使之与参数 name 相同
Thread.State getState();
返回该线程的状态
boolean isAlive();
测试线程是否处于活动状态
boolean isInterrupted();
测试线程是否已经中断
void interrupt();
中断线程
static boolean interrupted();
判断当前线程是否已经中断。线程的中断状态由该方法清除。
案例代码
public class Demo9 {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("烤羊排" + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("大盘鸡" + i);
if (i == 5) {
System.out.println("线程礼让");
Thread.yield();
}
}
}
});
// thread2.start();
thread1.start();
System.out.println("获取当前线程:" + Thread.currentThread());
System.out.println("获取线程1的标识:" + thread1.getId());
System.out.println("获取线程2的标识:" + thread2.getId());
System.out.println("获取当前线程的标识:" + Thread.currentThread().getId());
System.out.println("获取当前线程的名称:" + thread1.getName());
// 设置线程的名称
thread1.setName("线程1");
System.out.println("获取当前线程的名称:" + thread1.getName());
System.out.println("获取当前线程的状态:" + thread1.getState());
System.out.println("获取当前线程是否存活:" + thread1.isAlive());
System.out.println("获取当前线程是否中断:" + thread1.isInterrupted());
// 中断线程
thread1.interrupt();
System.out.println("获取当前线程是否中断:" + thread1.isInterrupted());
System.out.println("获取当前线程是否中断:" + Thread.interrupted());
// 主线程
System.out.println("main线程");
}
}
);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("大盘鸡" + i);
if (i == 5) {
System.out.println("线程礼让");
Thread.yield();
}
}
}
});
// thread2.start();
thread1.start();
System.out.println("获取当前线程:" + Thread.currentThread());
System.out.println("获取线程1的标识:" + thread1.getId());
System.out.println("获取线程2的标识:" + thread2.getId());
System.out.println("获取当前线程的标识:" + Thread.currentThread().getId());
System.out.println("获取当前线程的名称:" + thread1.getName());
// 设置线程的名称
thread1.setName("线程1");
System.out.println("获取当前线程的名称:" + thread1.getName());
System.out.println("获取当前线程的状态:" + thread1.getState());
System.out.println("获取当前线程是否存活:" + thread1.isAlive());
System.out.println("获取当前线程是否中断:" + thread1.isInterrupted());
// 中断线程
thread1.interrupt();
System.out.println("获取当前线程是否中断:" + thread1.isInterrupted());
System.out.println("获取当前线程是否中断:" + Thread.interrupted());
// 主线程
System.out.println("main线程");
}
}