进程(程序的一次执行)是系统进行资源分配和调度的基本单位。线程是轻量级进程,是程序执行的最小单位。一个进程可以包含多个线程。使用多线程是为了更好的利用cpu资源,提高执行效率和吞吐率。
线程相关状态我们从jdk代码可以看到:
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; } 复制代码
new:刚创建线程状态
runnable:可执行状态
blocked:阻塞状态(等待获取请求的锁),例如遇到synchronized同步块
waiting:无时间限制等待状态, 如
timed_waiting:有时间限制等待状态,如
terminated:结束状态(线程执行结束)
1.继承线程Thread,重写run()方法,如下使用匿名内部类的实现方式
Thread t1 = new Thread(){ @Override public void run() { System.out.printf("Hello World!"); } }; t1.start(); 复制代码
2.通过Thread类的带有Runnable接口参数的方法创建
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); } 复制代码
/** * Allocates a new {@code Thread} object. This constructor has the same * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} * {@code (null, target, gname)}, where {@code gname} is a newly generated * name. Automatically generated names are of the form * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer. * * @param target * the object whose {@code run} method is invoked when this thread * is started. If {@code null}, this classes {@code run} method does * nothing. */ public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } 复制代码
通过该构造函数,传入Runnable接口实例,调用start()方法,新线程即可执行,如下示例所示:
Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.printf("Hello World!"); } }); t1.start(); 复制代码
一般线程执行完毕会自动结束,无序手动关闭。但有些意外的情况不会终止,thread提供了stop()方法进行强制终止,方法如下所示:
/** * Forces the thread to stop executing. * <p> * If there is a security manager installed, its <code>checkAccess</code> * method is called with <code>this</code> * as its argument. This may result in a * <code>SecurityException</code> being raised (in the current thread). * <p> * If this thread is different from the current thread (that is, the current * thread is trying to stop a thread other than itself), the * security manager's <code>checkPermission</code> method (with a * <code>RuntimePermission("stopThread")</code> argument) is called in * addition. * Again, this may result in throwing a * <code>SecurityException</code> (in the current thread). * <p> * The thread represented by this thread is forced to stop whatever * it is doing abnormally and to throw a newly created * <code>ThreadDeath</code> object as an exception. * <p> * It is permitted to stop a thread that has not yet been started. * If the thread is eventually started, it immediately terminates. * <p> * An application should not normally try to catch * <code>ThreadDeath</code> unless it must do some extraordinary * cleanup operation (note that the throwing of * <code>ThreadDeath</code> causes <code>finally</code> clauses of * <code>try</code> statements to be executed before the thread * officially dies). If a <code>catch</code> clause catches a * <code>ThreadDeath</code> object, it is important to rethrow the * object so that the thread actually dies. * <p> * The top-level error handler that reacts to otherwise uncaught * exceptions does not print out a message or otherwise notify the * application if the uncaught exception is an instance of * <code>ThreadDeath</code>. * * @exception SecurityException if the current thread cannot * modify this thread. * @see #interrupt() * @see #checkAccess() * @see #run() * @see #start() * @see ThreadDeath * @see ThreadGroup#uncaughtException(Thread,Throwable) * @see SecurityManager#checkAccess(Thread) * @see SecurityManager#checkPermission * @deprecated This method is inherently unsafe. Stopping a thread with * Thread.stop causes it to unlock all of the monitors that it * has locked (as a natural consequence of the unchecked * <code>ThreadDeath</code> exception propagating up the stack). If * any of the objects previously protected by these monitors were in * an inconsistent state, the damaged objects become visible to * other threads, potentially resulting in arbitrary behavior. Many * uses of <code>stop</code> should be replaced by code that simply * modifies some variable to indicate that the target thread should * stop running. The target thread should check this variable * regularly, and return from its run method in an orderly fashion * if the variable indicates that it is to stop running. If the * target thread waits for long periods (on a condition variable, * for example), the <code>interrupt</code> method should be used to * interrupt the wait. * For more information, see * <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why * are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>. */ @Deprecated public final void stop() { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if (this != Thread.currentThread()) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } // A zero status value corresponds to "NEW", it can't change to // not-NEW because we hold the lock. if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise } // The VM can handle all thread states stop0(new ThreadDeath()); } 复制代码
我们看到该方法被标记为废弃,后期jdk可能会移除该方法,因为stop()方法比较暴力,强行终止线程,会引起数据不一致的问题。
如果需要停止一个线程,我们可以自行决定线程何时退出。例如定义一个变量(决定是否退出),线程中自行在相应的位置判断该变量来进行退出操作。
jdk对于线程停止执行也有更好的支持,那就是下面要说的线程中断。
线程中断不会使线程立即退出,而是给目标线程发送一个退出通知(设置中断标识位),然后由目标线程自行处理。
Thread类中与中断相关的方法如下:
/** * Interrupts this thread. * * <p> Unless the current thread is interrupting itself, which is * always permitted, the {@link #checkAccess() checkAccess} method * of this thread is invoked, which may cause a {@link * SecurityException} to be thrown. * * <p> If this thread is blocked in an invocation of the {@link * Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link * Object#wait(long, int) wait(long, int)} methods of the {@link Object} * class, or of the {@link #join()}, {@link #join(long)}, {@link * #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)}, * methods of this class, then its interrupt status will be cleared and it * will receive an {@link InterruptedException}. * * <p> If this thread is blocked in an I/O operation upon an {@link * java.nio.channels.InterruptibleChannel InterruptibleChannel} * then the channel will be closed, the thread's interrupt * status will be set, and the thread will receive a {@link * java.nio.channels.ClosedByInterruptException}. * * <p> If this thread is blocked in a {@link java.nio.channels.Selector} * then the thread's interrupt status will be set and it will return * immediately from the selection operation, possibly with a non-zero * value, just as if the selector's {@link * java.nio.channels.Selector#wakeup wakeup} method were invoked. * * <p> If none of the previous conditions hold then this thread's interrupt * status will be set. </p> * * <p> Interrupting a thread that is not alive need not have any effect. * * @throws SecurityException * if the current thread cannot modify this thread * * @revised 6.0 * @spec JSR-51 */ public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); } /** * Tests whether the current thread has been interrupted. The * <i>interrupted status</i> of the thread is cleared by this method. In * other words, if this method were to be called twice in succession, the * second call would return false (unless the current thread were * interrupted again, after the first call had cleared its interrupted * status and before the second call had examined it). * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if the current thread has been interrupted; * <code>false</code> otherwise. * @see #isInterrupted() * @revised 6.0 */ public static boolean interrupted() { return currentThread().isInterrupted(true); } /** * Tests whether this thread has been interrupted. The <i>interrupted * status</i> of the thread is unaffected by this method. * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if this thread has been interrupted; * <code>false</code> otherwise. * @see #interrupted() * @revised 6.0 */ public boolean isInterrupted() { return isInterrupted(false); } 复制代码
上面三个方法调用如下:
public void Thread.interrupt(); //中断线程(设置中断标志位) public boolean Thread.isInterrupted(); //判断是否被中断(检查中断标志位) public static boolean Thread.interrupted(); //判断是否被中断,并清除当前中断状态(检查中断标志位) 复制代码
如下简单示例代码可以看到中断异常发生后重置中断标识,下次循环即可检查出中断退出:
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(){ @Override public void run() { while (true) { if(Thread.currentThread().isInterrupted()){ System.out.println("Interrupted"); break; } try { Thread.sleep(2000); } catch (InterruptedException e) { System.out.println("Interrupted When Sleep"); //中断抛出异常会清除中断标识,在此设置中断状态,下次循环即可检查出中断在自定义适当地方退出 Thread.currentThread().interrupt(); } Thread.yield(); } } }; t1.start(); Thread.sleep(2000); t1.interrupt(); } 复制代码
JDK提供的等待和通知的方法签名如下:
/** * Causes the current thread to wait until another thread invokes the * {@link java.lang.Object#notify()} method or the * {@link java.lang.Object#notifyAll()} method for this object. * In other words, this method behaves exactly as if it simply * performs the call {@code wait(0)}. * <p> * The current thread must own this object's monitor. The thread * releases ownership of this monitor and waits until another thread * notifies threads waiting on this object's monitor to wake up * either through a call to the {@code notify} method or the * {@code notifyAll} method. The thread then waits until it can * re-obtain ownership of the monitor and resumes execution. * <p> * As in the one argument version, interrupts and spurious wakeups are * possible, and this method should always be used in a loop: * <pre> * synchronized (obj) { * while (<condition does not hold>) * obj.wait(); * ... // Perform action appropriate to condition * } * </pre> * This method should only be called by a thread that is the owner * of this object's monitor. See the {@code notify} method for a * description of the ways in which a thread can become the owner of * a monitor. * * @throws IllegalMonitorStateException if the current thread is not * the owner of the object's monitor. * @throws InterruptedException if any thread interrupted the * current thread before or while the current thread * was waiting for a notification. The <i>interrupted * status</i> of the current thread is cleared when * this exception is thrown. * @see java.lang.Object#notify() * @see java.lang.Object#notifyAll() */ public final void wait() throws InterruptedException { wait(0); } /** * Wakes up a single thread that is waiting on this object's * monitor. If any threads are waiting on this object, one of them * is chosen to be awakened. The choice is arbitrary and occurs at * the discretion of the implementation. A thread waits on an object's * monitor by calling one of the {@code wait} methods. * <p> * The awakened thread will not be able to proceed until the current * thread relinquishes the lock on this object. The awakened thread will * compete in the usual manner with any other threads that might be * actively competing to synchronize on this object; for example, the * awakened thread enjoys no reliable privilege or disadvantage in being * the next thread to lock this object. * <p> * This method should only be called by a thread that is the owner * of this object's monitor. A thread becomes the owner of the * object's monitor in one of three ways: * <ul> * <li>By executing a synchronized instance method of that object. * <li>By executing the body of a {@code synchronized} statement * that synchronizes on the object. * <li>For objects of type {@code Class,} by executing a * synchronized static method of that class. * </ul> * <p> * Only one thread at a time can own an object's monitor. * * @throws IllegalMonitorStateException if the current thread is not * the owner of this object's monitor. * @see java.lang.Object#notifyAll() * @see java.lang.Object#wait() */ public final native void notify(); 复制代码
这两个方法是 在Object类中 而不是Thread类中,任何对象都可调用。
一个线程调用object.wait()方法会进入object对象的等待队列(队列会有等待同一对象的多个线程)。当object.notify()时,会随机选择一个线程唤醒(还有一个notifyAll()方法会唤醒所有线程)。
下面看下示例:
public class SimpleWN { final static Object object = new Object(); public static class T1 extends Thread{ @Override public void run() { synchronized (object){ System.out.println(System.currentTimeMillis()+":T1 start"); try { System.out.println(System.currentTimeMillis()+":T1 wait for object"); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(System.currentTimeMillis()+":T1 end"); } } } public static class T2 extends Thread{ @Override public void run() { synchronized (object){ System.out.println(System.currentTimeMillis()+":T2 start! notify one thread"); object.notify(); System.out.println(System.currentTimeMillis()+":T2 end"); try { Thread.sleep(2000); } catch (InterruptedException e) { } } } } public static void main(String[] args) { Thread t1 = new T1(); Thread t2 = new T2(); t1.start(); t2.start(); } } 复制代码
上面示例输出为:
1594535225267:T1 start 1594535225268:T1 wait for object 1594535225268:T2 start! notify one thread 1594535225268:T2 end 1594535227268:T1 end 复制代码
T1 end 比T2 end间隔两秒,刚好是T2 Thread.sleep(2000),说明T2 notify()后T1并没有执行,而是T2释放锁以后T1再次重新获得object对象锁才会执行。
等待线程结束
当一个线程依赖其它线程执行完毕才能执行,JDK提供了join()操作:
//无限等待,阻塞当前线程,直到目标线程执行完毕 public final void join() throws InterruptedException; //参数为最大等待时间,超时目标线程未执行完成,也会继续执行 public final synchronized void join(long millis) throws InterruptedException; 复制代码
我们来看下join方法的简单示例:
public class JoinMain { public volatile static int i = 0; public static class AddThread extends Thread{ @Override public void run() { for (i = 0; i<10000000;i++); } } public static void main(String[] args) throws InterruptedException { AddThread at = new AddThread(); at.start(); at.join(); System.out.println(i); } } 复制代码
示例中主线程调用join()方法等待AddThread执行完毕,所以结果输出为10000000。如果不调用at.join(),则i的输出会是0或者很小的数字。 join方法的核心代码块如下:
if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } 复制代码
可以看到join()方法中调用了wait方法,所以上面的示例中main线程wait在AddThread线程对象实例上进行等待,AddThread线程执行完成退出前会调用notifyAll()唤醒main线程继续运行(关于在什么地方进行调用notifyAll进行唤醒调用线程(例如示例中main线程)?可以参考: stackoverflow.com/questions/9… )。
所以需要避免在程序中在Thread对象实例上进行wait()或者notify()操作,可能会影响join()方法的正常运行。
线程谦让
线程谦让Thread.yield()定义如下:
/** * A hint to the scheduler that the current thread is willing to yield * its current use of a processor. The scheduler is free to ignore this * hint. * * <p> Yield is a heuristic attempt to improve relative progression * between threads that would otherwise over-utilise a CPU. Its use * should be combined with detailed profiling and benchmarking to * ensure that it actually has the desired effect. * * <p> It is rarely appropriate to use this method. It may be useful * for debugging or testing purposes, where it may help to reproduce * bugs due to race conditions. It may also be useful when designing * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. */ public static native void yield(); 复制代码
Thread.yield()使当前线程让出cpu,但并不代表当前线程不执行了,还会竞争cpu资源(竞争到cpu资源会继续执行)。