程序协作:Thread多线程编程实践
今天我们来学习Java里的程序协作——Thread多线程编程实践。
程序中的线程运行框架体系
<1>线程创建
创建线程的方法有两种,第一种是继承Thread来实现,第二种是实现Runnable接口,接下来我们以具体的实例来讲述两种创建线程的方法。
代码清单13-1
ThreadDemo.java:
package com.manage.thread;
public class ThreadDemo extends Thread {
public static void main(String[] args) {
Thread th = Thread.currentThread();
System.out.println("主线程:" + th.getName());
ThreadDemo td = new ThreadDemo();
td.start();
}
@Override
public void run() {
System.out.println("子线程:" + this.getName());
}
}
输出结果:
主线程:main
子线程:Thread-0
代码解析:
这段代码很好理解,首先但凡是程序执行都会涉及线程,因为它是最小的执行单位。
那么在这段代码中,使用currentThread获取的就是main方法的线程,而ThreadDemo是新建一个类的实例,再使用start()方法开始执行线程。
不论如何都需要重写run()方法,因为它是自定义线程具体执行的内容,在这里我们使用getName()方法获取子线程的名称。
代码清单13-2
RunnableDemo.java:
package com.manage.thread;
public class RunnableDemo implements Runnable {
public static void main(String[] args) {
Thread th1 = Thread.currentThread();
System.out.println("主线程:" + th1.getName());
RunnableDemo rd = new RunnableDemo();
new Thread(rd, "第1个子线程").start();
new Thread(rd, "第2个子线程").start();
Thread th2 = new Thread(rd);
th2.start();
}
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
输出结果:
主线程:main
第1个子线程
第2个子线程
Thread-0
代码解析:
使用Runnable方式新建线程,同样的第一步输出了主线程的名称,接下来新建线程类的实例,分别启动2个线程并且赋予名称。接着,使用第二种方式启动线程,但它们都会去调用run()方法,来输出名称。
<2>线程调度
多线程并不是启动之后会维持一种状态,它也有自己的生命周期,而对线程的调度就是在线程生命周期内可以做得一些动作,如新建、就绪、运行、睡眠、等待、挂起、恢复、阻塞和死亡,在线程生命周期内修改线程的状态称作线程调度。
代码清单13-3
ThreadDispatchDemo.java:
package com.manage.thread;
public class ThreadDispatchDemo extends Thread {
Thread th = null;
public ThreadDispatchDemo() {
th = new Thread(this);
System.out.println("线程th状态是新建");
System.out.println("线程th状态是已经就绪");
th.start();
}
@Override
public void run() {
try {
System.out.println("线程th状态是正在运行");
Thread.sleep(5000);
System.out.println("线程th状态是在睡眠5秒之后,重新运行");
} catch (InterruptedException e) {
System.out.println("线程th状态是被终端:" + e.toString());
}
}
public static void main(String[] args) {
ThreadDispatchDemo td = new ThreadDispatchDemo();
}
}
输出结果:
线程th状态是新建
线程th状态是已经就绪
线程th状态是正在运行
线程th状态是在睡眠5秒之后,重新运行
代码解析:
这段代码先是分别定义了ThreadDispatchDemo类的构造器,在构造器里新建一个线程并且正式启动。接着在重写的run()方法里,通过睡眠的方式完成线程的调度,而具体的程序入口在main方法里,只需要新建一个ThreadDispatchDemo类的实例便可以完成触发。
<3>线程同步
线程同步实际上就是实现线程安全的过程,在程序中使用多线程的时候,因为不同的线程可能会请求同一个资源,如果不加以控制就会引发数据问题,因此我们需要给合适的线程加上synchronized关键字使其同步化,表示该线程所处理的资源已经加锁,需要等处理完毕解锁后才能被下一个线程处理。关于线程同步,我们使用购买者和生产者的概念来演示它,具体如代码清单所示。
代码清单13-4
Product.java:
package com.manage.synch;
public class Product {
private int id;
private String name;
}
代码解析:
本类用于创建商品数据模型类。
为它赋予id和name属性。
代码清单13-5
Customer.java:
package com.manage.synch;
public class Customer implements Runnable {
private Saleman saleman;
public Customer(Saleman saleman) {
this.saleman = saleman;
}
public void run() {
for (int i = 0; i < 10; i++) {
saleman.romoveProduct();
}
}
}
代码解析:
本类用于消费者购买商品,使用romoveProduct()进行减法运算。
代码清单13-6
Producter.java:
package com.manage.synch;
public class Producter implements Runnable {
private Saleman saleman;
public Producter(Saleman saleman) {
this.saleman = saleman;
}
public void run() {
for (int i = 0; i < 3; i++) {
saleman.addProduct(new Product());
}
}
}
代码解析:
本类用于生产者增加商品,使用addProduct()进行加法运算。
代码清单13-7
Saleman.java:
package com.manage.synch;
import java.util.ArrayList;
import java.util.List;
public class Saleman {
private List products = new ArrayList();
public synchronized void addProduct(Product product) {
while (products.size() > 2) {
System.out.println("货架已满,可以进行销售!");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
products.add(product);
System.out.println("销售员添加第" + products.size() + "个产品");
notifyAll();
}
public synchronized void romoveProduct() {
while (products.size() == 0) {
System.out.println("当前货物已卖完,请等待上货!");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("顾客买第" + products.size() + "产品");
products.remove(products.size() - 1);
notifyAll();
}
}
代码解析:
本类用于顾客购买和销售员上货这两种动作同时操作的场景,使用synchronized为不同的方法加锁,以防止引发数据问题。
代码清单13-8
ShopDemo.java:
package com.manage.synch;
public class ShopDemo {
public static void main(String[] args) {
Saleman saleman = new Saleman();
Producter producter = new Producter(saleman);
Customer customer = new Customer(saleman);
Thread producterOne = new Thread(producter);
Thread customerOne = new Thread(customer);
producterOne.start();
customerOne.start();
}
}
输出结果:
当前货物已卖完,请等待上货!
销售员添加第1个产品
销售员添加第2个产品
销售员添加第3个产品
顾客买第3产品
顾客买第2产品
顾客买第1产品
当前货物已卖完,请等待上货!
代码解析:
程序入口用于同时开启消费者和生产者的线程,因为对销售员和顾客所对应的方法都进行了线程同步,所以从输出结果可以看出并没有出现数据问题。