1. 线程基本概念
程序
是为了完成特定的任务,用某种语言编写的一组指令的集合.
简单说 : 就是我们写的代码
进程
- 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为
该进程分配内存空间。当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分
配新的内存空间 - 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的
产生、存在和消亡的过程
程序 —运行run—> 进程
线程
- 线程由进程创建的,是进程的一个实体
- 一个进程可以拥有多个线程
其他相关概念
- 单线程:同一个时刻,只允许执行一个线程
- 多线程:同一个时刻,可以执行多个线程,比如:一个QQ进程,可以同时打
开多个聊天窗口,一个迅雷进程,可以同时下载多个文件 - 并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说
单核cpu实现的多任务就是并发 - 并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行。并发和并行,
2. 线程基本使用
创建线程的三种方式
在java中线程来使用有两种方法
- 继承 Thread类,重写run方法
- 实现 Runnable接口,重写run方法

线程应用案例 1-继承 Thread 类
请编写程序开启一个线程,该线程毎隔1秒。在控制台输出“喵喵我是小猫咪
对上题改进:当输出80次喵喵,我是小猫咪,结束该线程
使用 JConsole 监控线程执行情况,并画岀程序示意图!

当一个类继承了 Thread 类, 该类就可以当做线程使用
我们会重写 run方法,写上自己的业务代码
run Thread 类 实现了 Runnable 接口的run方法
@Overridepublic void run() {if (target != null) {target.run();}}
public class Thread01 {public static void main(String[] args) throws InterruptedException {//创建Cat对象,可以当做线程使用Cat cat = new Cat();//老韩读源码/*(1)public synchronized void start() {start0();}(2)//start0() 是本地方法,是JVM调用, 底层是c/c++实现//真正实现多线程的效果, 是start0(), 而不是 runprivate native void start0();*/cat.start();//启动线程-> 最终会执行cat的run方法//cat.run();//run方法就是一个普通的方法, 没有真正的启动一个线程,就会把run方法执行完毕,才向下执行//说明: 当main线程启动一个子线程 Thread-0, 主线程不会阻塞, 会继续执行//这时 主线程和子线程是交替执行..System.out.println("主线程继续执行" + Thread.currentThread().getName());//名字mainfor(int i = 0; i < 60; i++) {System.out.println("主线程 i=" + i);//让主线程休眠Thread.sleep(1000);}}}
class Cat extends Thread {int times = 0;@Overridepublic void run() {//重写run方法,写上自己的业务逻辑while (true) {//该线程每隔1秒。在控制台输出 “喵喵, 我是小猫咪”System.out.println("喵喵, 我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName());//让该线程休眠1秒 ctrl+alt+ttry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if(times == 80) {break;//当times 到80, 退出while, 这时线程也就退出..}}}}
start0() 是本地方法,是JVM调用, 底层是c/c++实现真正实现多线程的效果, 是start0(), 而不是 run private native void start0();
run() 方法就是一个普通的方法, 没有真正的启动一个线程,就会把run方法执行完毕,才向下执行

线程应用案例 2-实现 Runnable 接口
- java是单继承的,在某些情况下一个类可能已经继承了某个父类这时在用继承 Thread 类方法来创建线程显然不可能了。
- java设计者们提供了另外一个方式创建线程,就是通过实现 Runnable 接口来创建线程
public class Thread02 {public static void main(String[] args) {Dog dog = new Dog();//dog.start(); 这里不能调用start//创建了Thread对象,把 dog对象(实现Runnable),放入ThreadThread thread = new Thread(dog);thread.start();// Tiger tiger = new Tiger();//实现了 Runnable// ThreadProxy threadProxy = new ThreadProxy(tiger);// threadProxy.start();}}class Animal {}class Tiger extends Animal implements Runnable {@Overridepublic void run() {System.out.println("老虎嗷嗷叫....");}}//线程代理类 , 模拟了一个极简的Thread类class ThreadProxy implements Runnable {//你可以把Proxy类当做 ThreadProxyprivate Runnable target = null;//属性,类型是 Runnable@Overridepublic void run() {if (target != null) {target.run();//动态绑定(运行类型Tiger)}}public ThreadProxy(Runnable target) {this.target = target;}public void start() {start0();//这个方法时真正实现多线程方法}public void start0() {run();}}class Dog implements Runnable { //通过实现Runnable接口,开发线程int count = 0;@Overridepublic void run() { //普通方法while (true) {System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());//休眠1秒try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (count == 10) {break;}}}}
线程应用案例 3-实现 Callable和Future接口
- 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
- 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
- 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
- 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
public class CallableThreadTest implements Callable<Integer> {public static void main(String[] args){CallableThreadTest ctt = new CallableThreadTest();FutureTask<Integer> ft = new FutureTask<>(ctt);for(int i = 0;i < 100;i++){System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);if(i==20){new Thread(ft,"有返回值的线程").start();}}try{System.out.println("子线程的返回值:"+ft.get());} catch (InterruptedException e){e.printStackTrace();} catch (ExecutionException e){e.printStackTrace();}}@Overridepublic Integer call() throws Exception{int i = 0;for(;i<100;i++){System.out.println(Thread.currentThread().getName()+" "+i);}return i;}}
静态代理模式
public class Thread02 {public static void main(String[] args) {Dog dog = new Dog();//dog.start(); 这里不能调用start//创建了Thread对象,把 dog对象(实现Runnable),放入ThreadThread thread = new Thread(dog);thread.start();// Tiger tiger = new Tiger();//实现了 Runnable// ThreadProxy threadProxy = new ThreadProxy(tiger);// threadProxy.start();}}class Animal {}class Tiger extends Animal implements Runnable {@Overridepublic void run() {System.out.println("老虎嗷嗷叫....");}}//线程代理类 , 模拟了一个极简的Thread类class ThreadProxy implements Runnable {//你可以把Proxy类当做 ThreadProxyprivate Runnable target = null;//属性,类型是 Runnable@Overridepublic void run() {if (target != null) {target.run();//动态绑定(运行类型Tiger)}}public ThreadProxy(Runnable target) {this.target = target;}public void start() {start0();//这个方法时真正实现多线程方法}public void start0() {run();}}class Dog implements Runnable { //通过实现Runnable接口,开发线程int count = 0;@Overridepublic void run() { //普通方法while (true) {System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());//休眠1秒try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (count == 10) {break;}}}}
线程使用应用案例-多线程执行
请编写一个程序创建两个线程一个线程每隔1秒输出“hell,word”:输出10次,退出,一个线程每隔1秒输出“hi”,输出5次退出
public class Thread03 {public static void main(String[] args) {T1 t1 = new T1();T2 t2 = new T2();Thread thread1 = new Thread(t1);Thread thread2 = new Thread(t2);thread1.start();//启动第1个线程thread2.start();//启动第2个线程//...}}class T1 implements Runnable {int count = 0;@Overridepublic void run() {while (true) {//每隔1秒输出 “hello,world”,输出10次System.out.println("hello,world " + (++count));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if(count == 60) {break;}}}}class T2 implements Runnable {int count = 0;@Overridepublic void run() {//每隔1秒输出 “hi”,输出5次while (true) {System.out.println("hi " + (++count));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if(count == 50) {break;}}}}
3. 继承 Thread vs 实现 Runnable 的区别
- 从java的设计来看,通过继承 Thread 或者实现 Runnable 接口来创建线程本质上没有区别从jdk帮助文档我们可以看到 Thread 类本身就实现了 Runnable 接口
start() -> start0() - 实现 Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用 Runnable
- [售票系统],编程模拟三个售票窗口售票100分别使用继承 Thread 和实现 Runnable 方式并分析有什么问题?
- 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
- 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
4. 线程终止
基本说明
当线程完成任务后,会自动退出
还可以通过使用变量来控制 run 方法退出的方式停止线程,即通知方式
应用案例
public class ThreadExit_ {public static void main(String[] args) throws InterruptedException {T t1 = new T();t1.start();//如果希望main线程去控制t1 线程的终止, 必须可以修改 loop//让t1 退出run方法,从而终止 t1线程 -> 通知方式//让主线程休眠 10 秒,再通知 t1线程退出System.out.println("main线程休眠10s...");Thread.sleep(10 * 1000);t1.setLoop(false);}}class T extends Thread {private int count = 0;//设置一个控制变量private boolean loop = true;@Overridepublic void run() {while (loop) {try {Thread.sleep(50);// 让当前线程休眠50ms} catch (InterruptedException e) {e.printStackTrace();}System.out.println("T 运行中...." + (++count));}}public void setLoop(boolean loop) {this.loop = loop;}}
5. 线程常用方法
常用方法第一组
setName设置线程名称,使之与参数name相同getName返回该线程的名称start使该线程开始执行;Java虚拟机底层调用该线程的start0方法run调用线程对象run方法setPriority更改线程的优先级getPriority获取线程的优先级sleep在指定的亳秒数内让当前正在执行的线程休眠(暂停执行)interrupt中断线程
注意事项和细节
start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程
线程优先级的范围
interrupt ,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
sleep : 线程的静态方法,使当前线程休眠
应用案例
public class ThreadMethod01 {public static void main(String[] args) throws InterruptedException {//测试相关的方法T t = new T();t.setName("老韩");t.setPriority(Thread.MIN_PRIORITY);//1t.start();//启动子线程//主线程打印5 hi ,然后我就中断 子线程的休眠for(int i = 0; i < 5; i++) {Thread.sleep(1000);System.out.println("hi " + i);}System.out.println(t.getName() + " 线程的优先级 =" + t.getPriority());//1t.interrupt();//当执行到这里,就会中断 t线程的休眠.}}class T extends Thread { //自定义的线程类@Overridepublic void run() {while (true) {for (int i = 0; i < 100; i++) {//Thread.currentThread().getName() 获取当前线程的名称System.out.println(Thread.currentThread().getName() + " 吃包子~~~~" + i);}try {System.out.println(Thread.currentThread().getName() + " 休眠中~~~");Thread.sleep(20000);//20秒} catch (InterruptedException e) {//当该线程执行到一个interrupt 方法时,就会catch 一个 异常, 可以加入自己的业务代码//InterruptedException 是捕获到一个中断异常.System.out.println(Thread.currentThread().getName() + "被 interrupt了");}}}}
常用方法第二组
- yield:线程的礼让。让出cpu,让其他线程执行,但礼
让的时间不确定,所以也不一定礼让成功 - join:线程的插队。插队的线程一旦插队成功,则肯定
先执行完插入的线程所有的任务
public class ThreadMethod02 {public static void main(String[] args) throws InterruptedException {T2 t2 = new T2();t2.start();for(int i = 1; i <= 20; i++) {Thread.sleep(1000);System.out.println("主线程(小弟) 吃了 " + i + " 包子");if(i == 5) {System.out.println("主线程(小弟) 让 子线程(老大) 先吃");//join, 线程插队//t2.join();// 这里相当于让t2 线程先执行完毕Thread.yield();//礼让,不一定成功..System.out.println("线程(老大) 吃完了 主线程(小弟) 接着吃..");}}}}class T2 extends Thread {@Overridepublic void run() {for (int i = 1; i <= 20; i++) {try {Thread.sleep(1000);//休眠1秒} catch (InterruptedException e) {e.printStackTrace();}System.out.println("子线程(老大) 吃了 " + i + " 包子");}}}
用户线程和守护线程
- 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
- 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
myDaemonThread.setDaemon(true); - 常见的守护线程:垃圾回收机制
应用案例
public class ThreadMethod03 {public static void main(String[] args) throws InterruptedException {MyDaemonThread myDaemonThread = new MyDaemonThread();//如果我们希望当main线程结束后,子线程自动结束//,只需将子线程设为守护线程即可myDaemonThread.setDaemon(true);myDaemonThread.start();for( int i = 1; i <= 10; i++) {//main线程System.out.println("宝强在辛苦的工作...");Thread.sleep(1000);}}}class MyDaemonThread extends Thread {public void run() {for (; ; ) {//无限循环try {Thread.sleep(1000);//休眠1000毫秒} catch (InterruptedException e) {e.printStackTrace();}System.out.println("马蓉和宋喆快乐聊天,哈哈哈~~~");}}}
6. 线程的生命周期
JDK 中用 Thread.State 枚举表示了线程的几种状态

线程状态转换图

查看线程状态
public class ThreadState_ {public static void main(String[] args) throws InterruptedException {T t = new T();System.out.println(t.getName() + " 状态 " + t.getState());t.start();while (Thread.State.TERMINATED != t.getState()) {System.out.println(t.getName() + " 状态 " + t.getState());Thread.sleep(500);}System.out.println(t.getName() + " 状态 " + t.getState());}}class T extends Thread {@Overridepublic void run() {while (true) {for (int i = 0; i < 10; i++) {System.out.println("hi " + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}break;}}}
7. 线程的同步
线程同步机制
- 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
- 也可以这里理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作
同步具体方法-Synchronized
- 同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码//需要被同步代码;}
- synchronized还可以放在方法声明中,表示整个方法-为同步方法
public synchronized void m(String name){//需要被同步的代码}
如何理解:
就好像某小伙伴上厕所前先把门关上(上锁)完事后再出来
解锁),那么其它小伙伴就可在使用厕所了使用 synchronized解决售票问题
分析同步原理

8. 互斥锁
基本介绍
- Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
- 每个对象都对应于一个可称为 “互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
- 关键字 synchronized来与对象的互斥锁联系。当某个对象用 synchronized 修饰时 ,表明该对象在任一时刻只能由一个线程访问
- 同步的局限性 : 导致程序的执行效率要降低
- 同步方法 ( 非静态的 ) 的锁可以是this,也可以是其他对象 ( 要求是同一个对象 )
- 同步方法 ( 静态的 ) 的锁为当前类本身。
使用互斥锁来解决售票问题
public class SellTicket {public static void main(String[] args) {//测试// SellTicket01 sellTicket01 = new SellTicket01();// SellTicket01 sellTicket02 = new SellTicket01();// SellTicket01 sellTicket03 = new SellTicket01();//// //这里我们会出现超卖..// sellTicket01.start();//启动售票线程// sellTicket02.start();//启动售票线程// sellTicket03.start();//启动售票线程// System.out.println("===使用实现接口方式来售票=====");// SellTicket02 sellTicket02 = new SellTicket02();//// new Thread(sellTicket02).start();//第1个线程-窗口// new Thread(sellTicket02).start();//第2个线程-窗口// new Thread(sellTicket02).start();//第3个线程-窗口//测试一把SellTicket03 sellTicket03 = new SellTicket03();new Thread(sellTicket03).start();//第1个线程-窗口new Thread(sellTicket03).start();//第2个线程-窗口new Thread(sellTicket03).start();//第3个线程-窗口}}//实现接口方式, 使用synchronized实现线程同步class SellTicket03 implements Runnable {private int ticketNum = 100;//让多个线程共享 ticketNumprivate boolean loop = true;//控制run方法变量Object object = new Object();//同步方法(静态的)的锁为当前类本身//老韩解读//1. public synchronized static void m1() {} 锁是加在 SellTicket03.class//2. 如果在静态方法中,实现一个同步代码块./*synchronized (SellTicket03.class) {System.out.println("m2");}*/public synchronized static void m1() {}public static void m2() {synchronized (SellTicket03.class) {System.out.println("m2");}}//老韩说明//1. public synchronized void sell() {} 就是一个同步方法//2. 这时锁在 this对象//3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在this对象public /*synchronized*/ void sell() { //同步方法, 在同一时刻, 只能有一个线程来执行sell方法synchronized (/*this*/ object) {if (ticketNum <= 0) {System.out.println("售票结束...");loop = false;return;}//休眠50毫秒, 模拟try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"+ " 剩余票数=" + (--ticketNum));//1 - 0 - -1 - -2}}@Overridepublic void run() {while (loop) {sell();//sell方法是一共同步方法}}}//使用Thread方式// new SellTicket01().start()// new SellTicket01().start();class SellTicket01 extends Thread {private static int ticketNum = 100;//让多个线程共享 ticketNum// public void m1() {// synchronized (this) {// System.out.println("hello");// }// }@Overridepublic void run() {while (true) {if (ticketNum <= 0) {System.out.println("售票结束...");break;}//休眠50毫秒, 模拟try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"+ " 剩余票数=" + (--ticketNum));}}}//实现接口方式class SellTicket02 implements Runnable {private int ticketNum = 100;//让多个线程共享 ticketNum@Overridepublic void run() {while (true) {if (ticketNum <= 0) {System.out.println("售票结束...");break;}//休眠50毫秒, 模拟try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票"+ " 剩余票数=" + (--ticketNum));//1 - 0 - -1 - -2}}}
注意事项和细节
- 同步方法如果没有使用 static修饰:默认锁对象为this
- 如果方法使用 static修饰,默认锁对象当前类cass
实现的落地步骤:
- 需要先分析上锁的代码
- 选择同步代码块或同步方法
- 要求多个线程的锁对象为同一个即可!
9. 线程的死锁
基本介绍
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁在编程是一定要避免死锁的发生
应用案例
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();}}//线程class DeadLockDemo extends Thread {static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用staticstatic Object o2 = new Object();boolean flag;public DeadLockDemo(boolean flag) {//构造器this.flag = flag;}@Overridepublic void run() {//下面业务逻辑的分析//1. 如果flag 为 T, 线程A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁//2. 如果线程A 得不到 o2 对象锁,就会Blocked//3. 如果flag 为 F, 线程B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁//4. 如果线程B 得不到 o1 对象锁,就会Blockedif (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");}}}}}
10. 释放锁
下面操作会释放锁
- 当前线程的同步方法、同步代码块执行结束
案例:上厕所,完事出来 - 当前线程在同步代码块、同步方法中遇到 break、 return
案例:没有正常的完事,经理叫他修改bug,不得已出来 - 当前线程在同步代码块、同步方法中出现了未处理的Eror或 Exception,导致异常结束
案例:没有正常的完事,发现忘带纸,不得已出来 - 当前线程在同步代码块、同步方法中执行了线程对象的wait0方法,当前线程暂停,并释
案例:没有正常完事,觉得需要酝酿下,所以出来等会再进去
下面操作不会释放锁
- 线程执行同步代码块或同步方法时,程序调用 Thread. sleep()、 Thread yield() 方法暂停当前线程的执行,不会释放锁
案例:上厕所,太困了,在坑位上眯了一会 - 线程执行同步代码块时,其他线程调用了该线程的 suspend 方法将该线程挂起,该线程不会释放锁
提示 : 应尽量避兔使用 suspend() 和 resume() 来控制线程,方法不再推荐使用
11. 作业

public class Homework01 {public static void main(String[] args) {A a = new A();B b = new B(a);//一定要注意.a.start();b.start();}}//创建A线程类class A extends Thread {private boolean loop = true;@Overridepublic void run() {//输出1-100数字while (loop) {System.out.println((int)(Math.random() * 100 + 1));//休眠try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("a线程退出...");}public void setLoop(boolean loop) {//可以修改loop变量this.loop = loop;}}//直到第2个线程从键盘读取了“Q”命令class B extends Thread {private A a;private Scanner scanner = new Scanner(System.in);public B(A a) {//构造器中,直接传入A类对象this.a = a;}@Overridepublic void run() {while (true) {//接收到用户的输入System.out.println("请输入你指令(Q)表示退出:");char key = scanner.next().toUpperCase().charAt(0);if(key == 'Q') {//以通知的方式结束a线程a.setLoop(false);System.out.println("b线程退出.");break;}}}}

public class Homework02 {public static void main(String[] args) {T t = new T();Thread thread1 = new Thread(t);thread1.setName("t1");Thread thread2 = new Thread(t);thread2.setName("t2");thread1.start();thread2.start();}}//编程取款的线程//1.因为这里涉及到多个线程共享资源,所以我们使用实现Runnable方式//2. 每次取出 1000class T implements Runnable {private int money = 10000;@Overridepublic void run() {while (true) {//解读//1. 这里使用 synchronized 实现了线程同步//2. 当多个线程执行到这里时,就会去争夺 this对象锁//3. 哪个线程争夺到(获取)this对象锁,就执行 synchronized 代码块, 执行完后,会释放this对象锁//4. 争夺不到this对象锁,就blocked ,准备继续争夺//5. this对象锁是非公平锁.synchronized (this) {////判断余额是否够if (money < 1000) {System.out.println("余额不足");break;}money -= 1000;System.out.println(Thread.currentThread().getName() + " 取出了1000 当前余额=" + money);}//休眠1stry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}
