一、基本概念
线程状态图包括五种状态
1、新建状态(New):线程对象被创建后,就进入新建状态。例如,Thread thread=new Thread();
2、就绪状态(Runnable):也被称为“可执行状态”。线程对象被创建后,其他线程调用了该对象的start()方法,从而来启动该线程。例如thread.start();处于就绪状态的线程,随时可能被CPU调度执行。
3、运行状态(Running):线程获取CPU权限进行执行。注意,线程只能从就绪状态进入到运行状态。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分为三种:
(1)、等待阻塞:通过调用线程的wait()方法,让线程等待某个工作的完成。
(2)、同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程所占用),它会进入同步阻塞状态。
(3)、其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了,或者因异常退出了run()方法,该线程结束生命周期。
二、实现多线程常用的两种方式:Thread和Runnable;【线程池(在java.util.concurrent包中)也可以实现多线程,后面讲解】
1、Thread和Runnable异同
(1)相同点:都是“多线程的实现方式”。
(2)不同点:Runnable是接口只包含一个Run()方法;实现方式可以定义一个类A实现该接口,通过new Thread(new A())等方式新建线程。Runnable接口代码如下:
public interface Runnable { public abstract void run(); }
Thread是实现了Runnable接口的一个类。如:public class Thread implements Runnable {}
我们知道“一个类只能有一个父类,但却能实现多个接口”,因此Runnable具有更好的可扩展性。此外,Runnable还可以用于“资源的共享”。即,多个线程都是基于某
一个Runnable对象建立的,它们会共享Runnable对象上建立的资源。通常建议通过Runnable实现多线程。
2、Thread多线程示例
示例代码如下:
import java.util.ArrayList; public class MyThread extends Thread { private int ticket = 10; @Override public void run() { for (int i = 0; i < 20; i++) { if (this.ticket>0){ System.out.println(this.getName()+"卖票:ticket"+this.ticket--); } } } } import org.junit.Test; public class ThreadTest { @Test public void demo1() { //启动三个线程,t1,t2,t3;每个线程各卖十张票 MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); // t1.run(); // t2.run(); // t3.run(); } }
Thread-1卖票:ticket10 Thread-2卖票:ticket10 Thread-1卖票:ticket9 Thread-0卖票:ticket10 Thread-1卖票:ticket8 Thread-2卖票:ticket9 Thread-1卖票:ticket7 Thread-0卖票:ticket9 Thread-1卖票:ticket6 Thread-2卖票:ticket8 Thread-1卖票:ticket5 Thread-1卖票:ticket4 Thread-0卖票:ticket8 Thread-1卖票:ticket3 Thread-2卖票:ticket7 Thread-1卖票:ticket2 Thread-1卖票:ticket1 Thread-0卖票:ticket7 Thread-0卖票:ticket6 Thread-0卖票:ticket5 ThThread-0卖票:ticket4
测试方法属于一个主线程,在里面创建了三个子线程,根据线程内安全,每个线程都会卖出10张票。
3、Runnable多线程示例
示例代码如下:
package RunnableDemo; public class MyThread implements Runnable { private int ticket = 10; public void run() { for (int i = 0; i < 20; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName()+ "卖票:ticket" + this.ticket--); } } } } package RunnableDemo; import ThreadDemo.ThreadTest; import org.junit.Test; public class RunnableTest { @Test public void demo1() { MyThread myThread = new MyThread(); //启动三个线程t1,t2,t3(共用一个Runnable对象),这三个线程一共卖10张票 Thread t1 = new Thread(myThread); Thread t2 = new Thread(myThread); Thread t3 = new Thread(myThread); t1.start(); t2.start(); t3.start(); } }
Thread-1卖票:ticket10 Thread-0卖票:ticket9 Thread-1卖票:ticket8 Thread-2卖票:ticket6 Thread-0卖票:ticket7 Thread-2卖票:ticket4 Thread-1卖票:ticket5 Thread-2卖票:ticket2 Thread-0卖票:ticket3 Thread-1卖票:ticket1
主线程创建并启动了三个子线程,而且这三个子线程都是基于“myThread这个Runnable对象”而创建的。运行结果显示这三个线程共同卖出10张票,说明它们共享了Runnable接口。
4、Thread中start()和run()方法区别
(1)、run()方法属于主线程方法,可以当做普通方法使用,在主线程中按照顺序执行,不新开线程,当上一个run()方法体执行完后,下一个run()方法才可以继续,没有达到多线程的目的;
(2)、start()方法来启动线程,真正实现了多线程运行。start()方法启动线程后,线程处于就绪状态,并未运行。然后通过Thread类调用实现Thread类的run()方法,run()方法运行结束标志线程终止。在run()方法执行过程中,其他子线程无需等待某子线程run()方法执行完成,而是由CPU调度执行哪个子线程。start()方法不能被重复调用,否则会抛IllegalThreadStateException异常
也可以通过运行代码,查看当前运行线程名字来判断
package StartVSRun; public class MyThread extends Thread { public MyThread(String name) { super(name); } @Override public void run() { System.out.println(Thread.currentThread().getName()+" is running"); } } package StartVSRun; import ThreadDemo.ThreadTest; import org.junit.Test; public class SVSR { @Test public void demo1() { Thread myThread = new MyThread("myThread"); System.out.println(Thread.currentThread().getName() + " call myThread.run()"); myThread.run(); System.out.println(Thread.currentThread().getName() + " call myThread.start()"); myThread.start(); } }
main call myThread.run()
main is running
main call myThread.start()
myThread is running