目录
java中的代码都是靠线程运行的,执行main方法的线程称为"主线程"。
~使用线程内容做一个聊天室项目~
-
客户端代码:
package socket;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* 聊天室客户端
*/
public class Client {
/*
java.net.Socket 套接字
Socket封装了TCP协议的通讯细节,我们通过它可以与远端计算机建立链接,
并通过它获取两个流(一个输入,一个输出),然后对两个流的数据读写完成
与远端计算机的数据交互工作。
我们可以把Socket想象成是一个电话,电话有一个听筒(输入流),一个麦克
风(输出流),通过它们就可以与对方交流了。
*/
private Socket socket;
/**
* 构造方法,用来初始化客户端
*/
public Client(){
try {
System.out.println("正在链接服务端...");
/*
实例化Socket时要传入两个参数
参数1:服务端的地址信息
可以是IP地址,如果链接本机可以写"localhost"
参数2:服务端开启的服务端口
我们通过IP找到网络上的服务端计算机,通过端口链接运行在该机器上
的服务端应用程序。
实例化的过程就是链接的过程,如果链接失败会抛出异常:
java.net.ConnectException: Connection refused: connect
*/
socket = new Socket("localhost",8088);
System.out.println("与服务端建立链接!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 客户端开始工作的方法
*/
public void start(){
try {
/*
Socket提供了一个方法:
OutputStream getOutputStream()
该方法获取的字节输出流写出的字节会通过网络发送给对方计算机。
*/
//低级流,将字节通过网络发送给对方
OutputStream out = socket.getOutputStream();
//高级流,负责衔接字节流与字符流,并将写出的字符按指定字符集转字节
OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
//高级流,负责块写文本数据加速
BufferedWriter bw = new BufferedWriter(osw);
//高级流,负责按行写出字符串,自动行刷新
PrintWriter pw = new PrintWriter(bw,true);
Scanner scanner = new Scanner(System.in);
while(true) {
String line = scanner.nextLine();
if("exit".equalsIgnoreCase(line)){
break;
}
pw.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
/*
通讯完毕后调用socket的close方法。
该方法会给对方发送断开信号。
*/
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Client client = new Client();
client.start();
}
}
-
服务端代码:
package socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 聊天室服务端
*/
public class Server {
/**
* 运行在服务端的ServerSocket主要完成两个工作:
* 1:向服务端操作系统申请服务端口,客户端就是通过这个端口与ServerSocket建立链接
* 2:监听端口,一旦一个客户端建立链接,会立即返回一个Socket。通过这个Socket
* 就可以和该客户端交互了
*
* 我们可以把ServerSocket想象成某客服的"总机"。用户打电话到总机,总机分配一个
* 电话使得服务端与你沟通。
*/
private ServerSocket serverSocket;
/**
* 服务端构造方法,用来初始化
*/
public Server(){
try {
System.out.println("正在启动服务端...");
/*
实例化ServerSocket时要指定服务端口,该端口不能与操作系统其他
应用程序占用的端口相同,否则会抛出异常:
java.net.BindException:address already in use
端口是一个数字,取值范围:0-65535之间。
6000之前的的端口不要使用,密集绑定系统应用和流行应用程序。
*/
serverSocket = new ServerSocket(8088);
System.out.println("服务端启动完毕!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 服务端开始工作的方法
*/
public void start(){
try {
System.out.println("等待客户端链接...");
/*
ServerSocket提供了接受客户端链接的方法:
Socket accept()
这个方法是一个阻塞方法,调用后方法"卡住",此时开始等待客户端
的链接,直到一个客户端链接,此时该方法会立即返回一个Socket实例
通过这个Socket就可以与客户端进行交互了。
可以理解为此操作是接电话,电话没响时就一直等。
*/
Socket socket = serverSocket.accept();
System.out.println("一个客户端链接了!");
/*
Socket提供的方法:
InputStream getInputStream()
获取的字节输入流读取的是对方计算机发送过来的字节
*/
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in,"UTF-8");
BufferedReader br = new BufferedReader(isr);
String message = null;
while((message = br.readLine())!=null) {
System.out.println("客户端说:" + message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
server.start();
}
}
需要注意的几个点:
1:
当客户端不再与服务端通讯时,需要调用socket.close()断开链接,此时会发送断开链接的信号给服务端。这时服务端的br.readLine()方法会返回null,表示客户端断开了链接。2:
当客户端链接后不输入信息发送给服务端时,服务端的br.readLine()方法是出于阻塞状态的,直到读取了一行来自客户端发送的字符串。
TCP的三次握手和四次挥手
-
客户端与服务端建立TCP链接时需要进行三次握手
-
双方断开链接时要进行四次挥手
-
多客户端链接
- 之前只有第一个连接的客户端可以与服务端说话。
- 原因:
- 服务端只调用过一次accept方法,因此只有第一个客户端链接时服务端接受了链接并返回了Socket,此时可以与其交互。
- 而第二个客户端建立链接时,由于服务端没有再次调用accept,因此无法与其交互。
package socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 聊天室服务端
*/
public class Server {
/**
* 运行在服务端的ServerSocket主要完成两个工作:
* 1:向服务端操作系统申请服务端口,客户端就是通过这个端口与ServerSocket建立链接
* 2:监听端口,一旦一个客户端建立链接,会立即返回一个Socket。通过这个Socket
* 就可以和该客户端交互了
*
* 我们可以把ServerSocket想象成某客服的"总机"。用户打电话到总机,总机分配一个
* 电话使得服务端与你沟通。
*/
private ServerSocket serverSocket;
/**
* 服务端构造方法,用来初始化
*/
public Server(){
try {
System.out.println("正在启动服务端...");
/*
实例化ServerSocket时要指定服务端口,该端口不能与操作系统其他
应用程序占用的端口相同,否则会抛出异常:
java.net.BindException:address already in use
端口是一个数字,取值范围:0-65535之间。
6000之前的的端口不要使用,密集绑定系统应用和流行应用程序。
*/
serverSocket = new ServerSocket(8088);
System.out.println("服务端启动完毕!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 服务端开始工作的方法
*/
public void start(){
try {
while(true) {
System.out.println("等待客户端链接...");
/*
ServerSocket提供了接受客户端链接的方法:
Socket accept()
这个方法是一个阻塞方法,调用后方法"卡住",此时开始等待客户端
的链接,直到一个客户端链接,此时该方法会立即返回一个Socket实例
通过这个Socket就可以与客户端进行交互了。
可以理解为此操作是接电话,电话没响时就一直等。
*/
Socket socket = serverSocket.accept();
System.out.println("一个客户端链接了!");
/*
Socket提供的方法:
InputStream getInputStream()
获取的字节输入流读取的是对方计算机发送过来的字节
*/
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String message = null;
while ((message = br.readLine()) != null) {
System.out.println("客户端说:" + message);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
server.start();
}
}
- 添加循环操作后,发现依然无法实现。
- 原因在于:
- 外层的while循环里面嵌套了一个内层循环(循环读取客户端发送消息),而循环执行机制决定了里层循环不结束,外层循环则无法进入第二次操作。
1、多线程
- 线程:
单一的顺序执行流程就是一个线程。
- 顺序执行:代码一句一句的先后执行。
- 多线程:
多个线程并发执行。
线程之间的代码是快速被CPU切换执行的,造成一种感官上"同时"执行的效果。
- 并发:
多个线程实际运行是走走停停的。线程调度程序会将CPU运行时间划分为若干个时间片段并尽可能均匀的分配给每个线程,拿到时间片的线程被CPU执行这段时间。当超时后线程调度程序会再次分配一个时间片段给一个线程使得CPU执行它。如此反复。由于CPU执行时间在纳秒级别,我们感觉不到切换线程运行的过程。所以微观上走走停停,宏观上感觉一起运行的现象成为并发运行!- 用途:
当出现多个代码片段执行顺序有冲突时,希望它们各干各的时就应当放在不同线程上"同时"运行
一个线程可以运行,但是多个线程可以更快时,可以使用多线程运行
线程的生命周期图:
2、创建线程有两种方式
1、方式一:继承Thread,并重写run方法
- 第一种创建线程的方式:继承Thread,并重写run方法
- 定义一个线程类,重写run方法,在其中定义线程要执行的任务(希望和其他线程并发执行的任务)。
优点:
结构简单,便于匿名内部类创建
缺点:
继承冲突:由于java单继承,会导致不能在继承其他类去复用方法,
这在实际开发中是非常不便的。耦合问题:定义线程的同时重写了run方法,会导致线程与线程任务绑定在了一起,
不利于线程的重用。- 注:启动该线程要调用该线程的start方法,而不是run方法!!!
package thread;
/**
* 此种线程创建方式的
* 优点: 结构简单,利于匿名内部类形式创建
* 缺点:
* 1、存在继承冲突问题
* java 是单继承的,这意味着继承了 Thread 就无法在再继承其他类了,
* 这在实际开发中存在着诸多不便( 实际上继承最主要的目的是为了复用方法 )
* PS:( 如果只有一个子类需要实现线程,而其他子类不需要实现线程,单独往超类里放一个线程,
* 那意味着所有子类都有线程的共性,可实际上只有一个子类使用,这违背了 面对对象设计的编程思想的)
*
* ( 这里我们想到了 超类 ,但 超类的意义是: 为了提供多个类中的 共性 存在的 )
* ( 不是共性的东西 我们是不能往超类上放的 )
*
* 2、继承Thread的同时重写run方法,将线程任务定义在run方法中,
* 这会导致线程与线程要执行的任务存在了必然的耦合关系,不利于线程的重(chong)用
*/
public class ThreadDemo1 {
public static void main(String[] args) {
//实例化两个线程
/*
此处实例化采用Thread去赋值
是多态的写法,向上造型,
毕竟MyThread1和2都是继承自Thread,所以将他俩当成线程看也是可以的
这是一种编程思想
*/
Thread t1 = new MyThread1();
Thread t2 = new MyThread2();
//调用线程的start将线程启动。
//注意:启动线程不能直接调用run方法!!!
t1.start();
t2.start();
}
}
class MyThread1 extends Thread{
public void run(){
for (int i=0;i<1000;i++){
System.out.println("你是谁啊?");
}
}
}
class MyThread2 extends Thread{
public void run(){
for (int i=0;i<1000;i++){
System.out.println("11111111111111111111");
}
}
}
2、方式二:实现Runnable接口单独定义线程任务
- 第二种创建线程的方式 (常用)
- 实现 Runnable 接口单独定义线程任务
- 将线程和任务分开干,这样 线程 与 线程要执行的任务 就不是必然的 耦合关系
------ (解除了耦合思想)
package thread;
/**
* 第二种创建线程的方式 (常用)
* 实现Runnable接口单独定义线程任务
*
* 将线程和任务分开干,这样 线程 与 线程要执行的任务 就不是必然的 耦合关系 (解除了耦合思想)
*/
public class ThreadDemo2 {
public static void main(String[] args) {
//实例化线程要执行的任务
Runnable r1 = new MyRunnable1();
Runnable r2 = new MyRunnable2();
//创建两条线程
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
//启动线程任务
t1.start();
t2.start();
}
}
class MyRunnable1 implements Runnable {
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("123");
}
}
}
class MyRunnable2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("11111111111111");
}
}
}
3、匿名内部类形式的线程创建
- 第一种方式:(无法使用lambda表达式)
- 继承Thread重写run方法
Thread是一个类,无法使用lambda表达式实现- 第二种方式:(可以使用lambda表达式)
- 实现Runnable接口重写run方法
可以用lambda表达式创建Runnable(因为Runnable是个接口)- 例:
- Thread t2 =new Thread(() - > {
for (int i=0;i<1000;i++){
System.out.println("Runnable匿名内部类");
}
});
package thread;
/**
* 使用匿名内部类完成线程的两种创建
*/
public class ThreadDemo3 {
public static void main(String[] args) {
//第一种方式:继承Thread重写run方法
//Thread是一个类,无法使用lambda表达式实现
Thread t1 = new Thread(){
public void run() {
for (int i=0;i<1000;i++){
System.out.println("Thread匿名内部类");
}
}
};
//第二种方式:实现Runnable接口重写run方法
/* Runnable r2 = new Runnable() {
public void run() {
for (int i=0;i<1000;i++){
System.out.println("Runnable匿名内部类");
}
}
};
*/
/*
Runnable r2 = ()->{
for (int i=0;i<1000;i++){
System.out.println("Runnable匿名内部类");
}
};
*/
//可以用lambda表达式创建Runnable(因为Runnable是个接口)
Thread t2 =new Thread(()->{
for (int i=0;i<1000;i++){
System.out.println("Runnable匿名内部类");
}
});
t1.start();
t2.start();
}
}
拓展知识:
java中的代码都是靠线程运行的,执行main方法的线程称为"主线程"。
- java中所有的代码都是靠线程执行的,main方法也不例外。
- JVM启动后会创建一条线程来执行main方法,并取名为“main",所以通常称它为:“主线程”
- 我们自己定义的线程在不指定名字的情况下系统会分配一个名字,
格式为“thread-X”(X是一个数)
- Thread有一个静态方法:
- Thread currentThread()
获取运行该方法的线程例:
Thread main = Thread.currentThread();
//主线程:Thread [ main , 5 , main ]
System.out.println("主线程:"+main);
// 线程 [ 线程名字 , 线程的优先级 , 线程组 ]
- 后期会学习到一个很重要的API:ThreadLocal,它可以使得我们在一个线程上跨越多个方法时共享数据使用,其内部要用到currentThread方法来辨别线程。如spring的事物控制就是靠ThreadLocal实现的。
package thread;
/**
* java中所有的代码都是靠线程执行的,main方法也不例外。
* JVM启动后会创建一条线程来执行main方法,并取名为“main",所以通常称它为:”主线程“
*
* 我们自己定义的线程在不指定名字的情况下系统会分配一个名字,
* 格式为“thread-X”(X是一个数)
*
* Thread有一个静态方法:
* Thread currentThread()
* 获取运行该方法的线程
*/
public class CurrentThreadDemo {
public static void main(String[] args) {
/*
后期会学习到一个很重要的API:ThreadLocal,它可以使得我们在一个线程上跨越多个
方法时共享数据使用,其内部要用到currentThread方法来辨别线程。
如spring的事物控制就是靠ThreadLocal实现的。
*/
/*
Thread有一个静态方法:
Thread currentThread()
获取运行该方法的线程
*/
//主线程执行main方法进来后会执行下面这句代码,此方法会的就是主线程
Thread main = Thread.currentThread();
System.out.println("主线程:"+main);
//主线程:Thread[ main , 5 , main ]
// 线程 [ 线程名字 , 线程的优先级 , 线程组 ]
dosome();
System.out.println("主线程执行完main方法后就结束了");
}
public static void dosome(){
//获取调用dosome方法的线程
Thread t = Thread.currentThread();
System.out.println("调用dosome方法的线程是:"+t);
}
}
3、使用多线程实现多客户端连接服务端
流程图:
服务端代码改造:
package socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 聊天室服务端
*/
public class Server {
/**
* 运行在服务端的ServerSocket主要完成两个工作:
* 1:向服务端操作系统申请服务端口,客户端就是通过这个端口与ServerSocket建立链接
* 2:监听端口,一旦一个客户端建立链接,会立即返回一个Socket。通过这个Socket
* 就可以和该客户端交互了
*
* 我们可以把ServerSocket想象成某客服的"总机"。用户打电话到总机,总机分配一个
* 电话使得服务端与你沟通。
*/
private ServerSocket serverSocket;
/**
* 服务端构造方法,用来初始化
*/
public Server(){
try {
System.out.println("正在启动服务端...");
/*
实例化ServerSocket时要指定服务端口,该端口不能与操作系统其他
应用程序占用的端口相同,否则会抛出异常:
java.net.BindException:address already in use
端口是一个数字,取值范围:0-65535之间。
6000之前的的端口不要使用,密集绑定系统应用和流行应用程序。
*/
serverSocket = new ServerSocket(8088);
System.out.println("服务端启动完毕!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 服务端开始工作的方法
*/
public void start(){
try {
while(true) {
System.out.println("等待客户端链接...");
/*
ServerSocket提供了接受客户端链接的方法:
Socket accept()
这个方法是一个阻塞方法,调用后方法"卡住",此时开始等待客户端
的链接,直到一个客户端链接,此时该方法会立即返回一个Socket实例
通过这个Socket就可以与客户端进行交互了。
可以理解为此操作是接电话,电话没响时就一直等。
*/
Socket socket = serverSocket.accept();
System.out.println("一个客户端链接了!");
//启动一个线程与该客户端交互
ClientHandler clientHandler = new ClientHandler(socket);
Thread t = new Thread(clientHandler);
t.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
server.start();
}
/**
* 定义线程任务
* 目的是让一个线程完成与特定客户端的交互工作
*/
private class ClientHandler implements Runnable{
private Socket socket;
public ClientHandler(Socket socket){
this.socket = socket;
}
public void run(){
try{
/*
Socket提供的方法:
InputStream getInputStream()
获取的字节输入流读取的是对方计算机发送过来的字节
*/
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String message = null;
while ((message = br.readLine()) != null) {
System.out.println("客户端说:" + message);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
4、线程API
获取线程相关信息的方法:
- 获取线程信息的一组方法:
- 获取主线程
- Thread main = Thread .currentThread () ;
- 获取线程的名字
- String name = main .getName () ;
- 获取该线程的唯一标识
- long id = main .getId () ; //不一定是long值
- 获取线程的优先级(1-10)
- int priority = main .getPriority () ;
- 线程是否还活着
- boolean isAlive = main .isAlive () ;
- 线程是否为守护线程
- boolean isDaemon = main .isDaemon () ;
- 线程是否被中断了
- boolean isInterrupted = main .isInterrupted () ;
- PS:
- ID:唯一标识 使用该值可以唯一标识一个个体。
- 可以做为唯一表示的值 必须同时具备两个条件:
- 1:非空 2:唯一
- 扩展:行规:
一般is开头的基本都是Boolean类型,get的变型
- 例:
isAlive isDaemon isInterrupted
package thread;
/**
* 获取线程信息的一组方法
*
* 扩展:行规:
* 一般is开头的基本都是Boolean类型,get的变型
* 例:isAlive isDaemon isInterrupted
*/
public class ThreadInfoDemo {
public static void main(String[] args) {
//获取主线程
Thread main = Thread.currentThread();
//获取线程的名字
String name = main.getName();
System.out.println("线程的名字:"+name);
/*
ID:唯一标识 使用该值可以唯一标识一个个体。
可以做为唯一表示的值 必须同时具备两个条件:
1:非空 2:唯一
*/
long id = main.getId();//不一定是long值
System.out.println("ID:"+id);
//获取线程的优先级(1-10)
int priority = main.getPriority();
System.out.println("线程的优先级:"+priority);
//线程是否还活着
boolean isAlive = main.isAlive();
//线程是否为守护线程
boolean isDaemon = main.isDaemon();
//线程是否被中断了
boolean isInterrupted = main.isInterrupted();
System.out.println("线程是否还活着:"+isAlive);
System.out.println("线程是否为守护线程:"+isDaemon);
System.out.println("线程是否被中断了:"+isInterrupted);
}
}
5、线程优先级
- 线程 start 后会纳入到线程调度器中统一管理,
线程只能被动的被分配时间片并发运行,
而无法主动索取时间片。
- 线程有10个优先级,使用整数1-10表示
1为最小优先级,10为最高优先级.5为默认值
线程调度器尽可能均匀的将时间片分配给每个线程。
调整线程的优先级可以最大程度的干涉获取时间片的几率,
优先级越高的线程获取时间片的次数越多,反之则越少
package thread;
/**
* 线程的优先级
*
* 线程有10个优先级,分别对应整数1-10
* 其中1为最低,10为最高,5位默认值。
*
* 线程调用start方法后就纳入到线程调度器中被统一管理,
* 此时线程只能被动的被分配时间片, 并发运行,而无法主动索取时间片
*
* 调度器会尽可能均匀的将时间片分配给每个线程。
* 调整线程的优先级可以最大程度额改善获取时间片的概率,
* 线程优先级越高的线程获取时间片的概率越高。
*/
public class PriorityDemo {
public static void main(String[] args) {
Thread norm = new Thread() {
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("nor");
}
}
};
Thread max = new Thread() {
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("max");
}
}
};
Thread min = new Thread() {
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("min");
}
}
};
//优先级只能对应整数1-10.传入其他数字会抛出异常,Priority规定
min.setPriority(Thread.MIN_PRIORITY);//最小优先级
// min.setPriority(1);//这是最小优先级,上面是为了优雅Priority自己设定的名字
max.setPriority(Thread.MAX_PRIORITY);//最大优先级
min.start();
max.start();
norm.start();
}
}
6、sleep阻塞
线程提供了一个静态方法:
static void sleep(long ms)
使运行该方法的线程进入阻塞状态指定的毫秒,超时后线程会自动回到RUNNABLE状态等待再次获取时间片并发运行.
package thread;
import java.util.Scanner;
/**
* 线程提供了一个静态的方法:
* static void sleep(long ms)
* 该方法可以让运行这个方法的线程出于阻塞状态指定毫秒,超时后线程会自动回到RUNNABLE状态。
* 在此等待获取时间片并发执行。
*
* 此处扩展一个面试题:
* Thread.sleep(1000);(1000ms=1s)
* 是不是就说明就阻塞了1秒钟,然后可以把它精确到1秒钟,作为一个间隔时间去循环它,是不是真的就是卡1秒?
* 答:
* 不是,是存在误差的
* 当Running调用了Sleep 形成阻塞,这个阻塞是准确的,
* 但是阻塞后回到了Runnable状态,这个时候要等调度器给你分配时间片,
* 这个在等待时间片的过程就是误差,所以做不到精确阻塞的
* 要想解决 得从硬件下手
*/
public class SleepDemo {
public static void main(String[] args) {
System.out.println("start");
/*
* 简易的倒计时程序
* 要求:
* 程序启动后输入一个整数,从该数字开始每秒递减,
* 到0时输出时间到,程序结束
*/
Scanner scanner = new Scanner(System.in);
//我的方法
// int i= scanner.nextInt();
// while (true) {
// i--;
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(i);
// if (i==0){
// break;
// }
// }
//老师的方法
for (int i = scanner.nextInt();i>0;i--){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("时间到");
System.out.println("end");
}
}
7、sleep方法处理异常
- sleep方法处理异常:
- InterruptedException.
- 当一个线程调用sleep方法处于睡眠阻塞的过程中,该线程的interrupt()方法被调用时,sleep方法会抛出该异常从而打断睡眠阻塞。
package thread;
/** interrupt()方法使用
* 当一个线程调用sleep方法出于睡眠阻塞的过程中,
* 如果该线程的interrupt()方法被调用,
* 那么该线程会立即中断睡眠阻塞,
* 并在sleep方法这里抛出中断异常
*/
public class SleepInterruptDemo2 {
public static void main(String[] args) {
Thread lin = new Thread(){
public void run(){
System.out.println("林:刚美容完,休息一下吧~");
try {
/*
lin线程调用sleep方法后会进入睡眠阻塞,
期间如果lin线程的interrupt方法被调用的话,
就会在sleep方法这里立即抛出中断异常,
并结束sleep阻塞,并跳到catch块
*/
Thread.sleep(30000000);
} catch (InterruptedException e) {
System.out.println("此处因其他因数导致sleep中断,就会报异常");
System.out.println("林:干嘛呢!干嘛呢!干嘛呢!都破了相了!");
}
System.out.println("正常情况下,会在sleep结束后执行这里");
System.out.println("林:醒了");
}
};
Thread huang = new Thread(){
public void run(){
System.out.println("黄:大锤80,小锤40,开始砸墙!");
for (int i=0;i<5;i++){
System.out.println("黄:80!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//这里正常sleep,不会出现异常,所以此处可以不写
//不调用interrupt,就不会出现异常,
// 这里是java中的语法,人家不知道你是否会不会调用interrupt,所以这里强行捕获异常
}
}
System.out.println("咣当!");
System.out.println("黄:大哥!搞定");
//让huang线程中断lin线程的睡眠阻塞
lin.interrupt();
}
};
lin.start();
huang.start();
}
}