今天我们来学习Java里的程序协作——Thread多线程编程实践。
程序协作:Thread多线程编程实践_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-4Product.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产品
当前货物已卖完,请等待上货!

代码解析:

程序入口用于同时开启消费者和生产者的线程,因为对销售员和顾客所对应的方法都进行了线程同步,所以从输出结果可以看出并没有出现数据问题。