Java线程
java线程的状态类别介绍
java中一个线程总共有6种状态,都在Thread类中的State枚举中定义:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
-
NEW:尚未启动的线程状态;还没有使用Thread.start()方法进行线程启动;
-
RUNNABLE:该线程处于一个可运行的状态,使用了Thread.start()方法进行线程启动;但也有可能该线程正在等待操作系统为它分配执行时间;
-
BLOCKED:该线程处于一个阻塞的状态,比如线程运行遇到了一个同步代码块,则会进入阻塞状态直到获取到一个排它锁资源;
-
WAITING:该线程处于一个无时间限制的等待状态(处于这种状态的线程不会被分配处理器执行时间):没有设置Timeout参数的Object::wait()方法;没有设置Timeout参数的Thread::join()方法;LockSupport::park()方法。
-
TIMED_WAITING:该线程处于一个有时间限制的等待状态(处于这种状态的线程不会被分配处理器执行时间):不过无须等待 被其他线程显式唤醒,在一定时间之后它们会由系统自动唤醒。
以下方法会让线程进入限期等待状态:
Thread::sleep()方法;
设置了Timeout参数的Object::wait()方法;
设置了Timeout参数的Thread::join()方法;
LockSupport::parkNanos()方法;
LockSupport::parkUntil()方法。 -
TERMINATED:线程已终止的状态,线程已经结束执行。
-
WAITING和BLOCKED的区别:“等待状态”与“阻塞状态”的区别是“阻塞状态”在等待着获取到一个排它锁进入同步代码块,这个事件将在另外一个线程放弃这个锁的时候发生;而“等待状态”则是在等待一段时间,或者唤醒动作的发生。
java线程基本操作
新建线程、启动线程、终止线程
- 最简单的两种新建线程方式:继承Thread类、实现Runnable接口。由于java是单继承的,也就是说继承本身是一种很宝贵的资源,因此我们在上述两种方式中更偏向选择实现Runnable类来创建一个线程。直接使用Thread t1 = new Thread(runnable)并进行t1.start()方法的调用,那么java虚拟机就会新建一个线程并执行runnable实例中重新的run方法;
- 线程Thread有一个run()方法,t1.start()线程启动后就会执行run方法,而如果直接使用t1.run()是不会创建一个线程的,而是在当前线程中串行调用run()方法,只是作为一个普通的方法调用。
- Thread类提供了一个stop()方法进行线程终止,但是该方法已经被标注为一个废弃的方法。原因是该方法停止线程的方式过于暴力,直接无视任何情况,直接停止该线程。假如线程正在给某一个对象的部分字段进行赋值,然后突然被强制停止关闭了该线程,那么该对象的数据就是不完整的。
线程中断
- 中断线程,设置中断状态标志:public void Thread interrupt();
- 判断线程是否被中断:public boolean Thread isInterrupted();
- 判断线程是否被中断,并清除中断状态标志:public static boolean Thread interrupted():
- interrup()方法只是设置了中断状态标志,具体的线程中断退出操作需要在线程中进行逻辑代码处理。比如使用Thread.currentThread.isInterrupted()方法就可以知道当前线程的中断状态标志,再根据实际情况进行中断操作处理即可;
- InterruptedException是一个中断异常,它不是运行时异常,也就是说程序必须捕获并进行处理。当线程在sleep()或者wait()时,如果被中断,这个异常就会发生。
public static native void sleep(long millis) throws InterruptedException;
public final native void wait(long timeout) throws InterruptedException;
假如在InterruptedException的异常捕获catch操作中,只是做了对资源进行释放的操作,那么建议最后重新设置中断状态Thread.currentThread.interrupted();以便让中断检查代码正确执行。
等待wait和通知notify
- wait和notify是jdk提供的两个多线程之间通信协作的方法;这两个方法属于Object类。
- 当在一个对象实例上调用了wait()方法后,obj.wait(),当前线程就会进入obj对象的等待队列,并转为等待状态。直到其他线程调用了obj.notify()方法为止。当notify()被调用时,它就会从这个等待队列中,随机选择一个线程,并将其唤醒。这个选择时不公平的,随机的。notifyAll()方法时唤醒等待队列中所有等待的线程,而不是随机选择一个。
- wait()和notify()方法的调用必须包含在对应的synchronizied语句中,都需要首先获得目标对象的一个监视器(monitor)。
- wait()方法会释放目标对象的锁,而THread.sleep()方法不会释放任何资源。
挂起suspend和继续执行resume
- 这两个操作时一对相反的操作,被suspend挂起的线程必须要等到resume才能继续执行;但是suspend挂起的线程期间并不会释放任何锁资源。
- 这两个方法已被标注为废弃方法。因为假如resume操作意外地在suspend前就执行了,那么被挂起的线程就难有机会继续执行了。并且,它所占用的锁也不会被释放,严重的话甚至影响整个系统正常工作。
等待线程结束join和谦让yield
- join()的本质是让调用线程wait()在当前线程对象实例上。下述代码中,调用线程就是main线程,线程对象实例就是at线程。所以main线程会在at线程对象实例上等待wait。直到at线程执行完毕后,就会在退出前调用notifyAll()通知所有的等待线程继续执行。main线程才会打印“main Thread end!”语句。
public static void main(String[] args) throw InterruptedException{
AddThread at = new AddThread();
at.start();
at.join();
System.out.println("main Thread end!");
}
- 第一个join()方法表示无限等待,他会一直阻塞调用线程,知道目标线程执行完毕。第二个方法给出了一个最大等待时间。
public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException
- yield()是Thread类的一个静态方法,他会使当前线程让出cpu,但是当前线程让出cpu后仍会进行cpu资源的争夺。
public static native void yield();