java多线程编程 - 实战篇(一)
来源于:转载自dev2dev网友axman的go deep into java专栏。
在进入实战篇以前,我们简单说一下多线程编程的一般原则。
[安全性]是多线程编程的首要原则
如果两个以上的线程访问同一对象时,一个线程会损坏另一个线程的数据,这就是违反了安全性原则,这样的程序是不能进入实际应用的。
安全性的保证可以通过设计安全的类和程序员的手工控制。如果多个线程对同一对象访问不会危及安全性,这样的类就是线程安全的类,在JAVA中比如String类就被设计为线程安全的类。而如果不是线程安全的类,那么就需要程序员在访问这些类的实例时手工控制它的安全性。
[可行性]是多线程编程的另一个重要原则
如果仅仅实现了安全性,程序却在某一点后不能继续执行或者多个线程发生死锁,那么这样的程序也不能作为真正的多线程程序来应用。
相对而言安全性和可行性是相互抵触的,安全性越高的程序,可性行会越低。要综合平衡。
[高性能] 多线程的目的本来就是为了增加程序运行的性能,
如果一个多线程完成的工作还不如单线程完成得快。那就不要应用多线程了。
高性能程序主要有以下几个方面的因素:
数据吞吐率,在一定的时间内所能完成的处理能力。
响应速度,从发出请求到收到响应的时间。
容量,指同时处理相同任务的数量。
安全性和可行性是必要条件,如果达到不这两个原则那就不能称为真正的多线程程序。而高性是多线程编程的目的,也可以说是充要条件。否则,为什么采用多线程编程呢?
[生产者与消费者模式]
首先以一个生产者和消费者模式来进入实战篇的第一节。
生产者和消费者模式中保护的是谁?
多线程编程都在保护着某些对象,这些个对象是"紧俏资源",要被最大限度地利用,这也是采用多线程方式的理由。在生产者消费者模式中,我们要保护的是"仓库",在我下面的这个例子中,
就是桌子(table)。
我这个例子的模式完全是生产者-消费者模式,但我换了个名字。厨师-食客模式,这个食堂中只有1张桌子,同时最多放10个盘子,现在有4个厨师做菜,每做好一盘就往桌子上放(生产者将产品往仓库中放),而有6个食客不停地吃(消费者消费产品,为了说明问题,他们的食量是无限的)。
一般而言,厨师200-400ms做出一盘菜,而食客要400-600ms吃完一盘。当桌子上放满了10个盘子后,所有厨师都不能再往桌子上放,而当桌子是没有盘子时,所有的食客都只好等待。
下面我们来设计这个程序:
因为我们不知道具体是什么菜,所以叫它food:
这一个例子中,我们主要关注以下几个方面:
1.同步方法要保护的对象,本例中是保护桌子,不能同时往上放菜或同时取菜。
假如我们把putFood方法和getFood方法在厨师类和食客类中实现,那么我们应该如此:
(以putFood为例)
在进入实战篇以前,我们简单说一下多线程编程的一般原则。
[安全性]是多线程编程的首要原则
如果两个以上的线程访问同一对象时,一个线程会损坏另一个线程的数据,这就是违反了安全性原则,这样的程序是不能进入实际应用的。
安全性的保证可以通过设计安全的类和程序员的手工控制。如果多个线程对同一对象访问不会危及安全性,这样的类就是线程安全的类,在JAVA中比如String类就被设计为线程安全的类。而如果不是线程安全的类,那么就需要程序员在访问这些类的实例时手工控制它的安全性。
[可行性]是多线程编程的另一个重要原则
如果仅仅实现了安全性,程序却在某一点后不能继续执行或者多个线程发生死锁,那么这样的程序也不能作为真正的多线程程序来应用。
相对而言安全性和可行性是相互抵触的,安全性越高的程序,可性行会越低。要综合平衡。
[高性能] 多线程的目的本来就是为了增加程序运行的性能,
如果一个多线程完成的工作还不如单线程完成得快。那就不要应用多线程了。
高性能程序主要有以下几个方面的因素:
数据吞吐率,在一定的时间内所能完成的处理能力。
响应速度,从发出请求到收到响应的时间。
容量,指同时处理相同任务的数量。
安全性和可行性是必要条件,如果达到不这两个原则那就不能称为真正的多线程程序。而高性是多线程编程的目的,也可以说是充要条件。否则,为什么采用多线程编程呢?
[生产者与消费者模式]
首先以一个生产者和消费者模式来进入实战篇的第一节。
生产者和消费者模式中保护的是谁?
多线程编程都在保护着某些对象,这些个对象是"紧俏资源",要被最大限度地利用,这也是采用多线程方式的理由。在生产者消费者模式中,我们要保护的是"仓库",在我下面的这个例子中,
就是桌子(table)。
我这个例子的模式完全是生产者-消费者模式,但我换了个名字。厨师-食客模式,这个食堂中只有1张桌子,同时最多放10个盘子,现在有4个厨师做菜,每做好一盘就往桌子上放(生产者将产品往仓库中放),而有6个食客不停地吃(消费者消费产品,为了说明问题,他们的食量是无限的)。
一般而言,厨师200-400ms做出一盘菜,而食客要400-600ms吃完一盘。当桌子上放满了10个盘子后,所有厨师都不能再往桌子上放,而当桌子是没有盘子时,所有的食客都只好等待。
下面我们来设计这个程序:
因为我们不知道具体是什么菜,所以叫它food:
- package debug;
- import java.util.regex.*;
- import java.util.*;
- class Food {
- }
- class Table extends LinkedList {
- int maxSize;
- public Table(int maxSize) {
- this.maxSize = maxSize;
- }
- public synchronized void putFood(Food f) {
- while (this.size() >= this.maxSize) {
- try {
- this.wait();
- } catch (Exception e) {
- }
- }
- this.add(f);
- notifyAll();
- }
- public synchronized Food getFood() {
- while (this.size() <= 0) {
- try {
- this.wait();
- } catch (Exception e) {
- }
- }
- Food f = (Food) this.removeFirst();
- notifyAll();
- return f;
- }
- }
- class Chef extends Thread {
- Table t;
- String name;
- Random r = new Random(12345);
- public Chef(String name, Table t) {
- this.t = t;
- this.name = name;
- }
- public void run() {
- while (true) {
- Food f = make();
- System.out.println(name + " put a Food:" + f);
- t.putFood(f);
- }
- }
- private Food make() {
- try {
- Thread.sleep(200 + r.nextInt(200));
- } catch (Exception e) {
- }
- return new Food();
- }
- }
- class Eater extends Thread {
- Table t;
- String name;
- Random r = new Random(54321);
- public Eater(String name, Table t) {
- this.t = t;
- this.name = name;
- }
- public void run() {
- while (true) {
- Food f = t.getFood();
- System.out.println(name + " get a Food:" + f);
- eat(f);
- }
- }
- private void eat(Food f) {
- try {
- Thread.sleep(400 + r.nextInt(200));
- } catch (Exception e) {
- }
- }
- }
- public class Test {
- public static void main(String[] args) throws Exception {
- Table t = new Table(10);
- new Chef("Chef1", t).start();
- new Chef("Chef2", t).start();
- new Chef("Chef3", t).start();
- new Chef("Chef4", t).start();
- new Eater("Eater1", t).start();
- new Eater("Eater2", t).start();
- new Eater("Eater3", t).start();
- new Eater("Eater4", t).start();
- new Eater("Eater5", t).start();
- new Eater("Eater6", t).start();
- }
- }
1.同步方法要保护的对象,本例中是保护桌子,不能同时往上放菜或同时取菜。
假如我们把putFood方法和getFood方法在厨师类和食客类中实现,那么我们应该如此:
(以putFood为例)