程序协作: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产品
当前货物已卖完,请等待上货!

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

posted @ 2020-11-09 18:01  51CTO学院  阅读(88)  评论(0编辑  收藏  举报