线程中常用方法总结与对比(一)
在介绍线程之前我们先了解一下线程的五大状态,如图所示:
从上图可以看出线程从创建到死亡的全部状态以及相应过程。
接下来我们了解一下线程中常用的方法以及区别
1.Thread.stop()
当我们想关闭当前正在执行的线程时,你会发现,Thread类中有stop(),可能你会直接使用这个方法去关闭线程,的确这是关闭线程最简单的方法,但是你的IDE可能会将这个方法标注为废弃的方法,这是为什么呢,原因是由于用stop()方法结束进程,进程会瞬间结束,不管当前线程是否执行完毕,这就会导致出现读写不一致的问题,我们来举个例子:
public class StopThreadUnsafe {
public static User user = new User();
public static class User {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User() {
id = 0;
name = "0";
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
public static class ChangeObjectThread extends Thread {
public void run() {
while (true) {
synchronized (user) {
int v = (int) (System.currentTimeMillis() / 1000);
user.setId(v);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
user.setName(v + "");
}
Thread.yield();
}
}
}
public static class ReadObjectThread extends Thread {
public void run() {
while (true) {
synchronized (user) {
if (user.getId() != Integer.parseInt(user.getName())) {
System.out.println(user.toString());
}
}
Thread.yield();
}
}
}
public static void main(String args[]) throws InterruptedException {
new ReadObjectThread().start();
while (true) {
Thread thread = new ChangeObjectThread();
thread.start();
Thread.sleep(150);
thread.stop();
}
}
}
运行结果
我们发现id与name并不想等,这是什么原因导致的,当线程调用stop()时,会立即终止并释放锁,当写操作将id写入时,线程终止,导致name并未写入,此时读操作获取锁并读出结果导致不一致的出现。
那么我们该如何结束线程呢
Thread 中提供了三个相关的方法:
void Thread.interrupt() //中断线程
boolean Thread.isInterrupted() //判断是否被中断
static boolean Thread.interrupted() //判断是否被中断,并清除当前中断状态
注: Thread.interrupt()只是在当前线程中加入一个中断标志位,Thread.isInterrupted()可以判断该标志位是否存在并对其进行自己的业务处理。
2.wait()和notify() notifyAll()
首先,这两个方法并不是线程Thread中的方法,而是Object中的方法,这就意味着任何对象都可以使用这两个方法。这两个方法的作用为:
当一个对象调用wait()方法后,当前线程就会停止继续执行,进入等待状态,并释放锁,直到其他线程中出现调用notify()时,该对象才有可能继续执行下去。值得注意的时,为什么说有可能执行下去呢:
在多线程通信时,可能存在许多线程同时进入wait()状态,此时其中一个线程调用notify()时, 那么所有wait()等待的线程只有其中一个可以重新获取锁并继续执行下去,这个幸运儿是随机的,并不是先等待先获取。并且当notify出现时,该并不是立即执行下去,而是要等当前的对象锁被其他人释放后再次获取成功之后在执行。这也就意味着,wait()和notify()必须在对应的synchronized语句中使用,这一点至关重要。
然而notifyAll()和notify()的区别在于notifyAll()会唤起所有wait()的线程。最后由图总结:
3.Thread.sleep()
当运行时线程调用Thread.sleep()方法时,该线程会进入阻塞状态,看上去和上面的wait()方法很像,他们之间的区别:
sleep()方法不会释放锁,当设定时间过去后,会继续执行下去
wait()会将当前的对象锁释放并进入等待队列,只有当出现notify或notifyAll时才会被唤醒重新去获取锁继续执行下去
4.jion()和yield()
jion()就像其英文一样为加入的意思,当我们想在一个或多个线程执行完之后使用他们操作之后的值来进行我们当前线程的输入,这时我们就可以考虑使用jion()来加入他们。
public class JoinMain {
public volatile static int i = 0;
public static class AddThread extends Thread {
public void run() {
System.out.println("add!");
for (i = 0; i < 1000000; i++) ;
}
}
public static void main(String args[]) throws InterruptedException {
AddThread at = new AddThread();
at.start();
at.join();
System.out.println(i);
}
}
如上结果可以看出当主线程想等待线程at执行之后使用i,就可以使用jion()等带他执行完成一起走下去,所以i的返回值永远是100000,如果将at.join();去掉的话,可能结果就会非常小,at线程还没走完主线程就已经结束了 这里Thread提供了jion的两个方法:
public final synchronized void join(long millis)
public final void join() throws InterruptedException
区别在于一个是当时间到了就不等了,另一个则是会一直等待着他的结束。
Thread.yield()方法则是会让出当前的CPU,重新去抢夺CPU资源,当一个线程我们认为重要的部分已经执行完成,后面的已经无关紧要,这时可以使用Thread.yield()方法重新去争抢资源,给其他线程成功执行的机会。
5.守护线程
守护线程是一些系统线程,比如GC回收,JIT线程等都称之为守护线程,用户线程为系统的工作线程,当用户线程全部结束后,不管守护线程是否依然存在,java虚拟机都会自然退出。
设置守护线程
Thread t = new Thread();
t.setDaemon(true); //必须在start启动之前设置,否则会设置失败抛出异常。
t.start();
个人总结,不喜勿喷;如有错误,望请见谅。