一、线程的编写方式
①继承Thread类
②实现Runnable接口(推荐使用,JAVA是单继承,如果该类已经继承了一个类了,那么就只能使用实现接口的方式)
class MyThread extends Thread{ @Override public void run() { System.out.println("继承Thread类"); } } class MyRunnable implements Runnable{ @Override public void run() { System.out.println("实现Runnable接口"); } } public class Test{ public static void main(String[] args) { MyThread myThread=new MyThread(); myThread.start(); Thread thread=new Thread(new MyRunnable()); thread.start(); System.out.println("main end"); } }
结果:
二、this和Thread.currentThread()各种方法的区别
this:当前对象
Thread.currentThread():当前线程
①currentThread()
class MyThread extends Thread{ @Override public void run() { System.out.println("this.currentThread:"+this.currentThread()); System.out.println("Thread.currentThread:"+Thread.currentThread()); } } public class Test{ public static void main(String[] args) { MyThread myThread=new MyThread(); Thread thread=new Thread(myThread); thread.start(); myThread.start(); } }
结果:
可以很明显地看到,无论是用一个线程包含另一个线程启动,还是一个线程单独启动,两个方法所得到当前线程都是一样的
注:这里面的main并不是指当前线程的名字
结论:两个方法都是获取当前线程,近乎等价
②getName()
class MyThread extends Thread{ @Override public void run() { try { Thread.sleep(1000); System.out.println("this.getName:"+this.getName()); System.out.println("Thread.currentThread.getName:"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Test{ public static void main(String[] args) throws InterruptedException { MyThread myThread=new MyThread(); Thread thread=new Thread(myThread); thread.start(); Thread.sleep(1000); myThread.start(); System.out.println("main线程已经结束"); } }
结果:
分析:
1)在源码中可以看见如下代码
线程的名字在调用构造函数的时候就已经确定,nextThreadNum()函数使用的是一个从0开始累加的一个static变量
2)结合结果和代码,可以推断出myThread的名字是Thread-0,而thread的名字是Thread-1。第一个开始运行的线程thread包裹了myThread,this指的是当前对象,所以this.getName()永远都是myThread的名字。而Thread.currentThread().getName()指的是外部线程(thread包裹了myThread)thread的名字。第二个运行的线程myThread中Thread.currentThread().getName()为Thread-0的原因是因为该线程没有被包裹(可以看见主线程已经早早结束),所以就是本身Thread-0
结论:this.getName()指的是当前对象的名字,Thread.currentThread().getName():①当有外部线程时,指的是外部线程的名字 ②没有外部线程时,指的是当前线程的名字
③isAlive()
class MyThread extends Thread{ @Override public void run() { try { Thread.sleep(1000); System.out.println("this.isAlive:"+this.isAlive()); System.out.println("Thread.currentThread.isAlive:"+Thread.currentThread().isAlive()); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Test{ public static void main(String[] args) throws InterruptedException { MyThread myThread=new MyThread(); myThread.run(); Thread.sleep(1000); myThread.start(); Thread.sleep(1000); Thread thread=new Thread(myThread); thread.start(); System.out.println("main线程已经结束"); } }
结果:
结论:判断线程是否存活
④getId()
class MyThread extends Thread{ @Override public void run() { try { Thread.sleep(1000); System.out.println("this.getId:"+this.getId()); System.out.println("Thread.currentThread.getId:"+Thread.currentThread().getId()); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Test{ public static void main(String[] args) throws InterruptedException { MyThread myThread=new MyThread(); myThread.run(); Thread.sleep(1000); myThread.start(); Thread.sleep(1000); Thread thread=new Thread(myThread); thread.start(); System.out.println("main线程已经结束"); } }
结果:
结论:获取线程的唯一标识
⑤interrupt、interrupted、isInterrupted
class ThreadA extends Thread{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println("i="+i); } } } public class Test{ public static void main(String[] args) { ThreadA threadA = new ThreadA(); Thread thread = new Thread(threadA); thread.start(); thread.interrupt(); System.out.println(thread.isInterrupted()); System.out.println(thread.interrupted()); System.out.println(thread.interrupted()); Thread.currentThread().interrupt(); System.out.println(thread.interrupted()); System.out.println(thread.interrupted()); } }
结果:
分析:thread.interrupted()前两次调用都为false,是因为其检测的是main线程(当前线程)是否中断,所以当主线程使用Thread.currentThread().interrupt()设置中断标志过后,再调用时第一次为true,最后一次为false则是因为调用一次后会清除中断标志
class ThreadA extends Thread{ @Override public void run() { System.out.println(this.isInterrupted()); if(!this.interrupted()){ for(int i=0;i<10;i++){ System.out.println("i="+i); } }else{ System.out.println("该线程已经设置中断"); System.out.println(this.isInterrupted()); } } } public class Test{ public static void main(String[] args) { ThreadA threadA = new ThreadA(); threadA.start(); threadA.interrupt(); } }
结果:
分析:第二次为false,是因为interrupted会清除中断标志,在主线程调用interrupt(),程序继续执行,是因为该方法并不会中断线程,仅仅是设置一个中断标志
总结:interrupt()给调用该方法的线程实例设置中断标志(并不会使线程停止), interrupted()是针对当前线程而言(调用后会清除中断标志),isInterrupted()指的是你调用该方法的那个线程实例
三、中止、暂停、恢复线程
①中止线程
1)return与抛出异常
class ThreadA extends Thread{ @Override public void run() { try{ if(this.interrupted()){ //return; throw new InterruptedException(); } System.out.println("线程还未中断"); }catch (Exception e){ System.out.println("捕获中断异常"); } } } public class Test{ public static void main(String[] args) { ThreadA threadA = new ThreadA(); threadA.start(); threadA.interrupt(); } }
结果:
分析:抛出异常和return能终止线程
2)sleep与interrupt
class ThreadA extends Thread{ @Override public void run() { try{ Thread.sleep(2000); System.out.println("线程还未中断"); }catch (Exception e){ System.out.println("捕获中断异常"); System.out.println(this.interrupted()); } } } public class Test{ public static void main(String[] args) throws InterruptedException { ThreadA threadA = new ThreadA(); threadA.start(); Thread.sleep(1000); threadA.interrupt(); } }
结果:
分析:interrupted()为false是因为进入catch语句会清除停止状态值,线程先睡眠后设置中断,会导致线程中断
class ThreadA extends Thread{ @Override public void run() { try{ for(int i=0;i<10;i++){ System.out.println(i); } Thread.sleep(1000); System.out.println("线程还未中断"); }catch (Exception e){ System.out.println("捕获中断异常"); } } } public class Test{ public static void main(String[] args){ ThreadA threadA = new ThreadA(); threadA.start(); threadA.interrupt(); System.out.println("main线程结束"); } }
结果:
分析:线程先中断后睡眠,也会导致线程中断
3)stop
class ThreadA extends Thread{ @Override public void run() { try{ this.stop(); System.out.println("线程还在执行"); }catch (ThreadDeath e){ System.out.println("捕获异常"); e.printStackTrace(); } } } public class Test{ public static void main(String[] args){ ThreadA threadA = new ThreadA(); threadA.start(); } }
结果:
分析:线程stop会抛出ThreadDeath异常
class ThreadA extends Thread{ @Override public void run() { try{ for(int i=0;i<50000;i++){ System.out.println(i); } System.out.println("线程还在执行"); }catch (ThreadDeath e){ System.out.println("ThreadA捕获异常"); e.printStackTrace(); } } } public class Test{ public static void main(String[] args){ ThreadA threadA = new ThreadA(); threadA.start(); try{ Thread.sleep(100); threadA.stop(); }catch (ThreadDeath e){ System.out.println("main捕获异常"); e.printStackTrace(); }catch (Exception e){ } } }
结果:
分析:虽然是在主线程调用该方法的,但是依旧是ThreadA抛出异常
结论:中止线程一共有4种方法:①return②stop③抛出异常④interrupt和sleep组合使用
②暂停、恢复线程
class ThreadA extends Thread{ public long i; @Override public void run() { while(true){ i++; } } } public class Test{ public static void main(String[] args) throws InterruptedException { ThreadA threadA = new ThreadA(); threadA.start(); Thread.sleep(1000); threadA.suspend(); System.out.println(System.currentTimeMillis()+";"+threadA.i); Thread.sleep(1000); System.out.println(System.currentTimeMillis()+";"+threadA.i); threadA.resume(); Thread.sleep(5000); System.out.println(System.currentTimeMillis()+";"+threadA.i); } }
结果:
结论:暂停和重启线程
四、线程切换
①yield
并不是马上切换(放弃cpu资源)
五、线程优先级
线程的优先级:从1-10,1的优先级最低,10的优先级最高
①setPriority
②getPriority
cpu有概率把资源先给优先级高的线程
六、守护线程
①setDaemon
class ThreadA extends Thread{ @Override public void run() { while(true){ try { System.out.println("还在执行"); this.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test{ public static void main(String[] args) throws InterruptedException { ThreadA threadA = new ThreadA(); threadA.setDaemon(true); threadA.start(); Thread.sleep(5000); System.out.println("main线程结束"); } }
结果:
结论:当线程设置为守护线程后,当非守护线程都结束时,守护线程会随着JVM一起结束