java笔记--超级类Object多线程的应用+哲学家进餐算法内部类与多线程结合

关于Object类中的线程方法:

 

Object类是所有Java类的 父类,在该类中定义了三个与线程操作有关的方法,使得所有的Java类在创建之后就支持多线程

这三个方法是:notify(),notifyAll(),wait(),这几个方法都是用来控制线程的运行状态的。

 

方法列表如下
notify() : 唤醒在此对象监视器上等待的单个线程
notifyAll() : 唤醒在此对象监视器上等待的所有线程
wait() : 在其他线程时调用此对象的notify()或者notifyAll()方法前,导致当前线程等待
wait(long timeout) : 在notify()或者notifyAll()方法被调用之前或者超过指定的时间之前,导致当前线程等待
wait(long timeout,int nanos) : 在notify()或者notifyAll()方法被调用之前或者超过指定的时间之前,
                                或者其他线程中断当前线程之前,导致当前线程等待。
--如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3907610.html "谢谢--                                
代码实例:

package com.xhj.thread;

import java.util.Random;

/**
 * Object类中与线程相关方法的应用
 * 
 * @author XIEHEJUN
 * 
 */
public class ObjectThreadMethod {
    /**
     * 定义商品最高件数
     */
    private int count = 10;
    /**
     * 生产时记录仓库商品件数
     */
    private int sum = 0;

    private class Producter implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < count; i++) {
                int num = new Random().nextInt(255);
                synchronized (this) {
                    if (sum == count) {
                        System.out.println("仓库已满");
                        try {
                            this.wait(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("生产商品" + num + "号");
                        this.notify();
                        sum++;
                        System.out.println("仓库还有商品" + sum + "件");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }

        }

    }

    private class Consomer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < count; i++) {
                synchronized (this) {
                    if (sum == 0) {
                        System.out.println("仓库已经为空,请补货");
                        try {
                            this.wait(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("消费着买去了一件商品");
                        this.notify();
                        sum--;
                        System.out.println("仓库还有商品" + sum + "件");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }

                    }
                }
            }

        }

    }

    /**
     * 调用线程服务
     */
    public void service() {

        Producter productor = new Producter();
        Consomer consomer = new Consomer();
        Thread thread1 = new Thread(productor);
        Thread thread2 = new Thread(consomer);
        thread1.start();
        thread2.start();

    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ObjectThreadMethod ob = new ObjectThreadMethod();
        ob.service();
    }

}

注意:  在Object类中,以上所有的方法都是final的,切记勿与Thread类混淆
        且这几个方法要与Synchronized关键字一起使用,他们都是与对象监视器有关的,
        当前线程必须拥有此对象的监视器,否则会出现IllegalMonitorStateException异常

 

下面将结合一个经典的算法实例来加深我们对于Object中多线程的方法的理解。

 

经典实例--哲学家进餐问题

有5个哲学家,每个哲学家的右手边有一根筷子,每个哲学家有两种状态,即思考和吃饭。

当哲学家想要吃饭时,必须要保证左右手的筷子都可用,否则哲学家进入等待状态。倘若

5个哲学家都想要吃饭,都处于等待状态,那么此时,线程发生死锁。倘若5个哲学家都不饿,

都在认真思考,那么此时线程进入活锁(即此时都在执行思考的线程,而吃饭的线程则只能等待有人来吃饭)

关系图:

_thumb3_thumb

 

从上图我们可以知道,根据问题描述及关系图,我们首先要建立两个关系类:筷子类--chopsticks哲学家类—Philosopher

为了能更好的将学过的知识融汇贯通并加以运用,在这里我将用内部类的形式完成这两个类之间的联系和调用

 

通过上面的关系图,我们知道筷子的可用与否,关系着哲学家所处的状态,以及将要执行的进程,因此,筷子的Available要设计成同步的

另外对于哲学家来说,思考和吃饭是不能同时进行的,两者只能选其一,或者两者皆无法选,只能进入等待,

因此,这两个方法thinking()和eatting()也必须是同步的

 

下面是详细的代码实例:

package com.xhj.thread;

import java.util.Random;

/**
 * 哲学家进餐算法(内部类和Object多线程的应用)
 * 
 * @author XIEHEJUN
 * 
 */
public class ChopsiticksAndPhilosophers {

    /**
     * 筷子实体类
     * 
     */
    private class Chopstick {
        /**
         * 筷子编号
         */
        private int id;
        /**
         * 筷子是否可用,默认为可用
         */
        private volatile boolean available = true;

        public Chopstick(int id) {
            this.id = id;
        }

        public int getId() {
            return id;
        }

        public void setAvailable(boolean available) {
            this.available = available;
        }

        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return id + "号筷子";
        }

    }

    /**
     * 哲学家类
     */
    private class Philosopyers implements Runnable {
        /**
         * 哲学家编号
         */
        private int id;
        /**
         * 筷子对象数组
         */
        private Chopstick[] chopsticks;
        /**
         * 哲学家状态--true表示正在思考;false表示吃饭或者等待吃饭
         */
        private volatile boolean state;

        /**
         * 获取哲学家左手边的筷子编号
         * 
         * @return
         */
        private Chopstick getLeftId() {
            return chopsticks[id];
        }

        /**
         * 获取哲学家右手边的筷子编号
         * 
         * @return
         */
        private Chopstick getRightId() {
            if (id == 0) {
                return chopsticks[chopsticks.length - 1];
            } else {
                return chopsticks[id - 1];
            }
        }

        public Philosopyers(int id, Chopstick[] chopsticks) {
            this.id = id;
            this.chopsticks = chopsticks;
        }

        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return id + "号哲学家";
        }

        /**
         * 哲学家正在思考
         */
        public synchronized void thinking() {
            if (state) {
                getLeftId().setAvailable(true);
                getRightId().setAvailable(true);
                System.out.println(id + "号哲学家正在思考");
                try {
                    // 思考1秒的时间
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            state = false;
        }

        /**
         * 哲学家在吃饭
         */
        public synchronized void eating() {
            if (!state) {
                if (getLeftId().available) {
                    if (getRightId().available) {
                        getLeftId().available = false;
                        getRightId().available = false;
                        System.out.println(id + "号哲学家在吃饭");
                        try {
                            // 吃饭吃一秒的时间
                            Thread.sleep(1000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("左边" + getRightId().getId()
                                + "号筷子不可用 " + id + "号专家进入等待状态");
                        try {
                            wait(new Random().nextInt(100));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                } else {
                    System.out.println("右边" + getLeftId().getId() + "号筷子不可用 "
                            + id + "号专家进入等待状态");
                    try {
                        wait(new Random().nextInt(100));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            state = true;
        }

        @Override
        public void run() {
            // 执行2次哲学家进餐以便更好的观察其过程(可根据需要修改)
            for (int i = 0; i < 2; i++) {
                System.out.print(i + "\t");
                thinking();
                eating();
            }
        }
    }

    /**
     * 哲学家进餐启动服务线程方法
     */
    public void service() {
        Chopstick[] chopsticks = new Chopstick[5];
        // 定义筷子数组
        for (int id = 0; id < 5; id++) {
            chopsticks[id] = new Chopstick(id);
        }
        // 5个哲学家,启动5个同步线程
        for (int id = 0; id < 5; id++) {
            Philosopyers phers = new Philosopyers(id, chopsticks);
            new Thread(phers).start();
        }
    }

    public static void main(String[] args) {
        ChopsiticksAndPhilosophers cap = new ChopsiticksAndPhilosophers();
        cap.service();
    }

}

运行结果:

结果

 

当我们看到这个结果时一定很惊讶,我们应该记得我们仅仅是设定了程序执行两次进餐行为,但是,这里却有三轮“进餐结果”,这又是为什么呢?

其实主要是因为那五位哲学家在第一轮的进餐中,都很累了,所以当要开始第二轮进餐的时候,他们集体跑去吃精神粮食了---思考,使得整个程序进入活锁状态,

即思考的进程一直在执行当中,而吃饭的进程虽然也在运行状态,但是却一直等待有人来吃饭,直到某一位哲学家实在饿得不行从思考中醒来之后,

才真正意义上开始了第二次进餐的行为。

posted @ 2014-08-12 16:31  Liape  阅读(1368)  评论(0编辑  收藏  举报