IO流(input /output)

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线程");
}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值