Java 主线程名
我们启动的一个程序可以理解为一个进程, 一个进程中包含一个主线程, 线程可以理解为一个子任务. Java 中可以通过下面代码来获取默认的主线程名.
System.out.println(Thread.currentThread().getName());
运行结果为 main
, 这是线程的名字并不是 main
方法, 通过此线程来执行 main
方法而已.
两种方式创建线程
1.继承 Thread 类
public class Thread1 extends Thread { @Override public void run() { System.out.println("qwe"); }}
2.实现 Runnable 接口
public class Thread2 implements Runnable { @Override public void run() { System.out.println("asd"); }}
Thread
实现Runnable
接口. 并且多线程运行时, 代码的执行顺序与调用顺序是无关的. 另外如果多次调用start
方法则会抛出java.lang.IllegalThreadStateException
currentThread 方法
返回当前代码正在被哪个线程调用.
public class Thread1 extends Thread { public Thread1() { System.out.println("构造方法的打印:" + Thread.currentThread().getName()); } @Override public void run() { System.out.println("run 方法的打印:" + Thread.currentThread().getName()); }}
Thread1 thread1 = new Thread1();thread1.start();
isAlive 方法
判断当前线程是否处于活动状态.
public class Thread1 extends Thread { @Override public void run() { System.out.println("run 方法的打印 Thread.currentThread().isAlive() == " + Thread.currentThread().isAlive()); System.out.println("run 方法的打印 this.isAlive() == " + this.isAlive()); System.out.println("run 方法的打印 Thread.currentThread().isAlive() == this.isAlive() == " + (Thread.currentThread() == this ? "true" : "false")); }}
Thread1 thread1 = new Thread1();System.out.println("begin == " + thread1.isAlive());thread1.start();Thread.sleep(1000);System.out.println("end == " + thread1.isAlive());
执行结果如下
begin == falserun 方法的打印 Thread.currentThread().isAlive() == truerun 方法的打印 this.isAlive() == truerun 方法的打印 Thread.currentThread() == this == trueend == false
thread1
在 1秒之后执行完成. 所以输出结果为 false
. 并且 Thread.currentThread()
和 this
是同一个对象, 可以理解成执行当前 run
方法的线程对象就是我们自己(this
).
如果将线程对象以构造参数的方式传递给 Thread 对象进行 start()
启动时, 运行结果和前面实例是有差异的. 造成这样的差异的原因还是来自于 Thread.currentThread()
和 this
的差异.
System.out.println("begin == " + thread1.isAlive());//thread1.start();// 如果将线程对象以构造参数的方式传递给 Thread 对象进行 start() 启动Thread thread = new Thread(thread1);thread.start();Thread.sleep(1000);System.out.println("end == " + thread1.isAlive());
执行结果
begin == falserun 方法的打印 Thread.currentThread().isAlive() == truerun 方法的打印 this.isAlive() == falserun 方法的打印 Thread.currentThread() == this == falseend == false
Thread.currentThread().isAlive()
是 true
的原因是因为, Thread.currentThread()
返回的是 thread
对象, 而我们也是通过此对象来启动线程的, 所以是在活动状态.
this.isAlive()
是 false
就比较好理解了, 因为我们是通过 thread
对象启动的线程来执行 run
方法的. 所以它是 false
. 同时也说明这两个不是同一个对象.
sleep 方法
在指定的毫秒数内让当前 "正在执行的线程" 休眠. "正在执行的线程" 只得是 Thread.currentThread()
返回的线程.
Thread.sleep(1000);
停止线程
停止一个线程意味着在线程处理完任务之前停掉正在做的操作, 也就是放弃当前的操作.
在 Java 中有以下 3 种方法可以终止正在运行的线程:
- 使用退出标志, 使线程正常退出, 也就是当 run 方法完成后线程终止.
- 使用 stop 方法强行终止线程, 但是不推荐使用这个方法.
- 使用 interrupt 方法中断线程.
停不了的线程
调用 interrupt 方法仅仅是当前线程打了一个停止标记, 并不是真正的停止线程.
public class Thread1 extends Thread { @Override public void run() { for(int i = 0; i < 500000; i++) { System.out.println(i); } }}
Thread1 thread1 = new Thread1();thread1.start();Thread.sleep(2000);thread1.interrupt();
我们两秒后调用 interrupt
方法, 根据打印结果我们可以看到线程并没有停止, 而是在执行完 500000 此循环后 run 方法结束线程停止.
判断线程是否是停止状态
我们将线程标记为停止后, 需要在线程内部判断一下这个线程是否是停止标记, 如果是则停止线程.
两种判断方法:
- Thread.interrupted(); 也可以使用 this.interrupted();
- this.isInterrupted();
下面是两个方法的源码:
public static boolean interrupted() { return currentThread().isInterrupted(true); } public boolean isInterrupted() { return isInterrupted(false); }
interrupted()
方法数据静态方法, 也就是判断当前线程是否已经中断. isInterrupted()
判断线程是否已经被中断.
来自官网的 interrupted()
方法重点. 线程的 中断状态 由该方法清除. 换句话说, 如果连续两次调用该方法, 则第二次调用将返回 false (在第一次调用已清除了其中断状态之后, 且第二次调用检验完中断状态前, 当前线程再次中断的情况除外).
异常停止线程
public class Thread1 extends Thread { @Override public void run() { try { for (int i = 0; i < 500000; i++) { if (this.isInterrupted()) { System.out.println("线程停止"); throw new InterruptedException(); } System.out.println("i = " + i); } } catch (InterruptedException e) { System.out.println("线程通过 catch 停止"); e.printStackTrace(); } }}
Thread1 thread1 = new Thread1();thread1.start();Thread.sleep(1000);thread1.interrupt();
输出结果, 这是最后几行:
i = 195173i = 195174i = 195175线程停止线程通过 catch 停止java.lang.InterruptedException at Thread1.run(Thread1.java:9)
当然我们也可以将throw new InterruptedException();
换成return
. 都是一样可以结束线程的.
在沉睡中停止
如果线程调用 Thread.sleep()
方法使线程进行休眠, 这时我们调用 thread1.interrupt()
后会抛出. InterruptedException
异常.
java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at Thread1.run(Thread1.java:8)
暴力停止线程
暴力停止线程可以使用 stop
方法, 但此方法已经过时并不推荐使用, 原因如下.
- 即刻抛出 ThreadDeath 异常, 在线程的run()方法内, 任何一点都有可能抛出ThreadDeath Error, 包括在 catch 或 finally 语句中. 也就是说代码不确定执行到哪一步就会抛出异常.
- 释放该线程所持有的所有的锁. 这可能会导致数据不一致性.
public class Thread1 extends Thread { private String userName = "a"; private String pwd = "aa"; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public void run() { this.userName = "b"; try { Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } this.pwd = "bb"; }}
Thread1 thread1 = new Thread1();thread1.start();Thread.sleep(1000);thread1.stop();System.out.println(thread1.getUserName() + " " + thread1.getPwd());
输出结果为:
b aa
我们在代码中然线程休眠 Thread.sleep(100000);
是为了模拟一些其它业务逻辑处理所用的时间, 在线程处理其它业务的时候, 我们调用 stop
方法来停止线程.
线程是被停止了也执行了 System.out.println(thread1.getUserName() + " " + thread1.getPwd());
来帮我们输出结果, 但是 this.pwd = "bb";
并没有被执行.
所以, 当调用了 stop
方法后, 线程无论执行到哪段代码, 线程就会立即退出, 并且会抛出 ThreadDeath
异常, 而且会释放所有锁, 从而导致数据不一致的情况.
interrupt
相比 stop
方法更可控, 而且可以保持数据一致, 当你的代码逻辑执行完一次, 下一次执行的时候, 才会去判断并退出线程.
如果大家不怎么理解推荐查看 这篇文章. 下面是另一个比较好的例子.
如果线程当前正持有锁(此线程可以执行代码), stop之后则会释放该锁. 由于此错误可能出现在很多地方, 那么这就让编程人员防不胜防, 极易造成对象状态的不一致. 例如, 对象 obj 中存放着一个范围值: 最小值low, 最大值high, 且low不得大于high, 这种关系由锁lock保护, 以避免并发时产生竞态条件而导致该关系失效.
假设当前low值是5, high值是10, 当线程t获取lock后, 将low值更新为了15, 此时被stop了, 真是糟糕, 如果没有捕获住stop导致的Error, low的值就为15, high还是10, 这导致它们之间的小于关系得不到保证, 也就是对象状态被破坏了!
如果在给low赋值的时候catch住stop导致的Error则可能使后面high变量的赋值继续, 但是谁也不知道Error会在哪条语句抛出, 如果对象状态之间的关系更复杂呢?这种方式几乎是无法维护的, 太复杂了!如果是中断操作, 它决计不会在执行low赋值的时候抛出错误, 这样程序对于对象状态一致性就是可控的.
suspend 与 resume 方法
用来暂停和恢复线程.
独占
public class PrintObject { synchronized public void printString(){ System.out.println("begin"); if(Thread.currentThread().getName().equals("a")){ System.out.println("线程 a 被中断"); Thread.currentThread().suspend(); } if(Thread.currentThread().getName().equals("b")){ System.out.println("线程 b 运行"); } System.out.println("end"); }}
try{ PrintObject pb = new PrintObject(); Thread thread1 = new Thread(pb::printString); thread1.setName("a"); thread1.start(); thread1.sleep(1000); Thread thread2 = new Thread(pb::printString); thread2.setName("b"); thread2.start();}catch(InterruptedException e){ }
输出结果:
begin线程 a 被中断
当调用 Thread.currentThread().suspend();
方法来暂停线程时, 锁并不会被释放, 所以造成了同步对象的独占.