Java的线程,即一个Thread实例。
Java的线程执行过程有两种实现方式:
创建一个新线程必须实例化一个Thread对象。
使用方法:
例:Thread使用方法
/** * Thread类。 */ class MyThread extends Thread { public void run() { //... 线程执行 } } public class TestThread1 { public static void main(String args[]) { MyThread thread = new MyThread (); //新建线程。 thread .start(); //线程开始执行。 //主线程其他方法 } }
实现Runnable接口需要实现run()方法.run()方法代表线程需要完成的任务.因此把run方法称为线程执行体.
使用方法:
例:Runnable使用方法
class MyRunner implements Runnable { //实现Runnable接口 public void run() { // ...... 线程执行 } } public class TestThread1 { public static void main(String args[]) { MyRunner runner= new Runner1(); Thread t = new Thread(runner); //新建线程。 t.start(); //线程开始执行。 // ...... 主线程继续执行 } }
实现Runnable接口方式的多线程:
继承Thread类方式的多线程:
1 Thread类的构造方法
2常用方法
(1) 启动线程
(2) 线程状态控制
(3) 当前线程
(4) 线程组
(5) 其他
1 新建和就绪状态
当程序使用new关键字创建了一个线程后,该线程就处于新建状态。JVM为Thread对象分配内存。初始化其成员变量的值。线程对象调用start()方法之后,该线程处于就绪状态。 JVM会为其创建方法调用栈和程序计数器。
就绪状态的线程并没有开始运行,它只是表示该线程可以运行了。JVM的线程调度器调度该线程运行。
注意:
2 运行和阻塞状态
就绪状态的线程获得了CPU,开始执行run方法的线程执行体。则该线程处于运行状态。线程在执行过程中可能会被中断,以使其他线程获得执行的机会,线程调度取决于底层平台采用的策略。
现代桌面和服务器操作系统一般都采用抢占式策略。一些小型设备如手机则可能采用协作式调度。 抢占式策略的系统:系统给每个可执行的线程一个小时间段来处理任务;当该时间段用完,系统会剥夺该线程所占有的资源,让其他线程获得执行机会.在选择下一个线程时,系统会考虑线程的优先级.
3 线程进入阻塞状态
4 阻塞线程重写进入就绪状态
5 线程死亡
线程在以下情况下死亡:
Thread对象的isAlive()方法,查看线程是否死亡。
注意:
6 线程睡眠:sleep
sleep方法将线程转入阻塞状态。时间到即再转入就绪状态。
yeild() 方法是静态方法。该方法使当前线程让出CPU资源,继续参与线程竞争。
public final void join() throws InterruptedException public final synchronized void join(long millis) throws InterruptedException
join()方法表示当前线程等待指定线程的结束,等待的线程结束后继续执行当前线程。
例: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(); // 当前线程(主线程)等待at线程运行结束。 System.out.println(i); } }
join的本质
查看join方法的源码,可以了解到,join即相当于以下代码:
while ( 指定的线程.isAlive()) { wait(0); }
当前线程会一直调用wait()方法,所以当前线程会一直处于等待状态。当被等待的线程结束(即指定的线程结束),JVM会自动调用notifyAll()方法来通知当前线程wait已经结束。注意,此处不要手动调用thread.notifyAll()方法。(关于join方法,JDK文档有更详细的说明)
每个线程都有优先级。优先级高的线程获得较多的执行机会。默认情况下,main线程具有普通优先级。每个线程默认优先级与创建它的父线程具有同样的优先级。
Java提供的优先级范围是1~10。默认优先级为5。
Thread提供静态常量:
注意:
运行在后台,用于为其他线程提供服务。又称为“守护线程”。所有前台线程死亡时,后台线程随之死亡。
比较:
常用API
若需要停止线程,不推荐使用stop()方法。stop方法强制线程停止,切线程会释放所有monitor。由于stop方法过于粗暴,已经被废弃。
例:现在有两条记录。两条线程的其中一个线程要写对象,另一个线程要读对象。两个线程对对象加锁。
若使用stop方法,可能导致数据一致性的错误。
public class StopThreadTest { static Student stu = new Student(); public static void main(String[] args) throws InterruptedException { Thread writeThread = new Thread(){ @Override public void run() { // 假设writeThread比readThread先拿到stu的锁。 synchronized(stu){ stu.id = 2; // 可能该语句执行完后,主线程中 writeThread.stop(); 开始产生作用。 // 若主线程执行 writeThread.stop();则该线程在此处将强行结束。 java_label_stop: stu.name = "小王"; } } }; Thread readThread = new Thread(){ @Override public void run() { // 假设writeThread比readThread先拿到stu的锁。 synchronized(stu){ // 若在java_label_stop处writeThread被结束,则该语句将打印出 2 ; 小明 。从而导致数据一致性错误 System.out.println(stu.id + ";" + stu.name); } } }; // 主线程设置stu。 stu.id = 1; stu.name = "小明"; // 假设writeThread比readThread先拿到stu的锁。 writeThread.start(); readThread.start(); // 强制停止写线程。 writeThread.stop(); } } class Student{ public int id; public String name; }
public void Thread.interrupt() // 中断线程。修改线程的中断状态,但线程本身不会有任何响应,依旧运行。 public boolean Thread.isInterrupted() // 判断是否被中断。 public static boolean Thread.interrupted() // 判断是否被中断,并清除当前中断状态
调用线程中断方法只是给线程“打个招呼”,线程不会本身不会做任何相应。
例:调用interrupt()方法,线程依旧运行。
public class InterruptThreadTest { public static void main(String[] args) { Thread t1 = new Thread(){ @Override public void run(){ while(true){ Thread.yield(); } } }; t1.start(); // 启动线程。 t1.interrupt(); // 终端线程,线程会依旧运行。 } }
线程中断方法可以用于结束线程,非常优雅方便。
例:线程外部使用中断方法,结束线程。
public class InterruptStopThreadTest { public static void main(String[] args) { Thread t1 = new Thread() { @Override public void run() { while (true) { // 检测线程被中断 if (Thread.currentThread().isInterrupted()) { // 若线程中断,则推出run方法。 System.out.println("Interruted!"); break; } Thread.yield(); } } }; t1.start(); // 启动线程。 t1.interrupt(); // 终端线程,但线程依旧运行。t1.isInterrupted()方法将返回true。 } }
大部分线程的等待方法都会抛出InterruptedException异常,中断标志位将会被清空。Java方法中默认的等待线程一旦被interrupt(),则等待方法会立即抛出InterruptedException异常。
例:处于sleep的线程被interrupt,立即抛出InterruptedException。
public class InterruptedExceptionTest { 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("Interruted!"); break; } try { Thread.sleep(2000); } catch (InterruptedException e) { System.out.println("Interruted When Sleep"); // 抛出异常后会清除中断标记位。所以重新设置中断状态。 Thread.currentThread().interrupt(); } Thread.yield(); } } }; t1.start(); // 让主线程在恰当的时间去中断t1。 Thread.sleep(1000); // 此时t1处于sleep状态。interrupt() 方法会使t1中的sleep方法抛出InterruptedException异常。 t1.interrupt(); } }
打印输出:
Interruted When Sleep
Interruted!
suspend() 方法表示将线程挂起。resume() 方法表示继续执行挂起的线程。
但需要注意suspend()不会释放锁。如果加锁发生在resume()之前,则发生死锁。
这两个方法都已经被废弃,不要使用。
例:运行以下程序,程序将被锁死。
package sjq.thread.suspend_resume; package sjq.thread.suspend_resume; public class SuspendResumeThreadTest { public static Object u = new Object(); public static ChangeObjectThread t1 = new ChangeObjectThread("t1"); public static ChangeObjectThread t2 = new ChangeObjectThread("t2"); public static class ChangeObjectThread extends Thread { public ChangeObjectThread(String name){ super.setName(name); } @Override public void run() { synchronized(u){ System.out.println("in " + super.getName()); Thread.currentThread().suspend(); } } } public static void main(String[] args) throws InterruptedException { t1.start(); Thread.sleep(100); t2.start(); t1.resume(); // 主线程通知t1要释放resume,但t1线程可能还未执行suspend,当t1执行了suspend后,则t1将永远被挂起。且不会释放资源。 t2.resume(); // 主线程通知t2要释放resume,但t2线程可能还未执行suspend,当t2执行了suspend后,则t2将永远被挂起。且不会释放资源。 t1.join(); t2.join(); } }
控制台显示:
该程序通过jstack查看线程情况,发现t2线程处于RUNNABLE状态,被suspend0方法挂起。且t2拥有Object的锁,只要t2线程不结束,Object的锁就不会被释放。