一、基本概念
线程状态图包括五种状态
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示例代码
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();
}
}
Runnable示例代码
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
运行结果