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

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();    }}

运行结果:

结果

 

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

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

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

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