多线程是Java语言的一个重要特征,利用多线程技术可以使系统同时运行多个程序块,缩短了程序的响应时间,提高了计算机资源的利用率,达到了多任务处理的目的。
进程与线程
- 进程:进程是程序的一次动态执行过程,每个程序都有自己独特的内存空间,一个应用程序可以启动多个应用空间,我们拿Chrome来举例。
- 线程:线程是进程中的一个执行流程,一个进程可以由多个线程组成,即一个进程中可以同时运行多个不同的线程,每个线程完成不同的任务。
没有进程就不会有线程,进程与线程是整体与局部的关系,进程与线程的关系如图1-1所示。
线程的生命周期
线程在完整的生命周期中要经历5种状态,分别是新建、就绪、运行、阻塞和死亡。状态关系如图1-2所示。
实现线程的两种方式
- 继承Thread类
在java.lang包中定义了Thread类,一个类继承了Thread类,此类就称为多线程实现类。
例子:
Test_Thread.java
package com.hcybx;
public class Test_Thread extends Thread{//继承Thread
private String name;
public Test_Thread(String name) {
this.name = name;
}
public void run() {//覆盖Thread中的run()方法
for (int i = 1; i < 5; i++) {
System.out.println(name+"运行,i="+i);
}
}
}
Test.java
package com.hcybx;
public class Test {
public static void main(String[] args) {
Test_Thread tt1 = new Test_Thread("A");//实例化线程对象
Test_Thread tt2 = new Test_Thread("B");
Test_Thread tt3 = new Test_Thread("C");
Test_Thread tt4 = new Test_Thread("D");
Test_Thread tt5 = new Test_Thread("E");
Test_Thread tt6 = new Test_Thread("F");
Test_Thread tt7 = new Test_Thread("G");
Test_Thread tt8 = new Test_Thread("H");
tt1.run();//启动多线程
tt2.run();
tt3.run();
tt4.run();
tt5.run();
tt6.run();
tt7.run();
tt8.run();
}
}
运行结果:
2. 实现Runnable接口(使用多个线程共享资源,利用Runnable来完成)
通过实现Runnable接口来实现多线程。
Runnale接口的定义:
package com.hcybx;
public class Test_Runnable implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
}
}
利用Runnable子类来启动多线程分为三个步骤。
1.实例化Runnable子类对象。
2.利用Runnable对象作为参数,实例化Thread对象。
3.利用Thread对象的start方法启动线程。
继承Thread类和实现Runnable接口的资源共享区别。(金典例题:卖飞机票)
1.通过继承Thread类来实现卖飞机票
package com.hcybx;
public class ThreadSell extends Thread {
private int ticket = 3;// 假如还剩三张飞机票
private String sellName;// 售票机
public ThreadSell(String sellName) {
this.sellName = sellName;
}
public void run() {
for (int i = 0; i < 20; i++) {
if (ticket > 0) {
System.out.println(sellName + "卖出第" + ticket-- + "张票");
}
}
}
}
2.通过实现Runnable接口来实现卖飞机票
package com.hcybx;
public class RunnableSell implements Runnable {
private int ticket = 3;
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket-- + "张票");
}
}
}
}
3.启动线程
package com.hcybx;
public class SellEngine {
//Thread实现多线程
public static void sellThread() {
ThreadSell sell1 = new ThreadSell("售票机一");
ThreadSell sell2 = new ThreadSell("售票机二");
ThreadSell sell3 = new ThreadSell("售票机三");
ThreadSell sell4 = new ThreadSell("售票机四");
ThreadSell sell5 = new ThreadSell("售票机五");
sell1.start();
sell2.start();
sell3.start();
sell4.start();
sell5.start();
}
//Runnable实现多线程
public static void sellRunnable() {
RunnableSell sell = new RunnableSell();
Thread t1 = new Thread(sell, "售票机一");
Thread t2 = new Thread(sell, "售票机二");
Thread t3 = new Thread(sell, "售票机三");
Thread t4 = new Thread(sell, "售票机四");
Thread t5 = new Thread(sell, "售票机五");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
4.开始卖票
package com.hcybx;
public class TestSell {
public static void main(String[] args) {
System.out.println("Thread方式");
SellEngine.sellThread();
try {
Thread.sleep(2000);//线程执行暂停2秒
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable方式");
SellEngine.sellRunnable();
}
}
运行结果:
所以我们在实际开发中,实现Runnable接口相比继承Thread类可以避免单继承带来的局限。因此一般采用实现Runnable接口来实现多线程。
同步
再次回到飞机票的问题,假设全国飞机票系统中,存在多个人同时订同一个机次的飞机票,那么机票是共享资源,每个人的预购系统是一个线程,这样存在多个线程对一个资源进行操作的问题。
问题引出:
package com.hcybx;
public class SellTicket implements Runnable{
private int ticket = 5; //假设还有5张票
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (ticket > 0) {
try {
Thread.sleep(1000);//延迟
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("卖出第"+ticket--+"张票");
}
}
}
}
package com.hcybx;
public class TestSell {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
Thread t3 = new Thread(st);
t1.start();
t2.start();
t3.start();
}
}
多次运行结果:
出现上面的根本原因就是程序代码被多个线程共享而交替运行,解决它的关键是==确保共享的代码块在某个时间被一个线程所拥有。==在Java语言中就可以利用同步解决问题。
package com.hcybx;
public class SellTicket implements Runnable {
private int ticket = 5; // 假设还有5张票
@Override
public void run() {
for (int i = 0; i < 20; i++) {
synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(1000);// 延迟
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖出第" + ticket-- + "张票");
}
}
}
}
}
package com.hcybx;
public class TestSell {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st,"售票点一");
Thread t2 = new Thread(st,"售票点二");
Thread t3 = new Thread(st,"售票点三");
t1.start();
t2.start();
t3.start();
}
}
使用同步代码块就可以解决共享资源时的正确性问题啦!