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个哲学家都不饿,

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

关系图:

从上图我们可以知道,根据问题描述及关系图,我们首先要建立两个关系类:筷子类--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();
    }

}

运行结果:

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

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

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

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

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

时间: 2024-11-12 19:02:50

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

java笔记--String类对象解析与运用

1.String中的equals和==的区别 String是对象而非基本数据类型,不能使用"=="来判断两个字符串是否相当, 判断两个字符串内容是否相同用equals(); 判断两个字符串内存地址是否相同用"==" 2.startsWith(String str): 判断字符串是否以str为前缀 3.endsWith(String str): 判断字符串是否以str为后缀 4.String 字符串的比较: 1).compareTo()和compareToIgnore

java笔记--String类格式化当天日期转换符文档

String类格式化当天日期 转换符:%tb : 格式化为月份的英文缩写%tB : 格式化为月份的英文全写%ta : 格式化为星期%tA : 格式化为星期%ty : 格式化为两位年份值%tY : 格式化为四位年份值%tm : 格式化为两位月份值%td : 格式化为两位日期值%te : 格式化为星期一位日期值%tH : 格式化为两位24时制小时值(00-23)%tk : 格式化为两位24时制小时值(0-23)%tI : 格式化为两位12时制小时值(01-12)%tl : 格式化为两位12时制小时值

Java笔记--常用类

1.String类: --使用Unicode字符编码,一个字符占两个字节: --String类是一个final类,代表不可变的字符序列: --字符串是不可变的,一个字符串对象一旦被配置,其内容是不可变的: --常用方法:1)int length();//返回字符串长度 2)char charAt(int index);//返回字符串在index处的字符 3)int compareTo(String str);//与str比较大小 4)int indexOf(String s);//返回s在字符串

关于java的根类Object详细分析

Object类是Java中其他所有类的祖先,没有Object类Java面向对象无从谈起.作为其他所有类的基类,Object具有哪些属性和行为,是Java语言设计背后的思维体现. Object类位于java.lang包中,java.lang包包含着Java最基础和核心的类,在编译时会自动导入.Object类没有定义属性,一共有13个方法,具体的类定义结构如下图: 1.类构造器public Object(); 大部分情况下,Java中通过形如 new A(args..)形式创建一个属于该类型的对象.

JAVA笔记10__Math类、Random类、Arrays类/日期操作类/对象比较器/

/** * Math类.Random类.Arrays类:具体查JAVA手册...... */ public class Main { public static void main(String[] args) { String[] s1 = {"a","b","c","d","e"}; String[] s2 = {"a","b","c",&qu

Java笔记:类与方法

一.对象引用 Java不允许使用指针,取而代之的是对象引用.对象引用可理解为指向对象的指针,但无法像真实的指针一样指向内存的任意位置,也不能像操作地址那样操作对象引用.除基本类型外的所有类型均为对象,所有的对象又均为引用类型,对象引用是Java安全性的关键. class Solution { public static void main(String[] args) { Object obj;//声明对象引用 obj = new Object();//实例化对象 } } 二.垃圾回收 Java

疯狂java笔记-枚举类

1 //手动实现枚举类 2 //步骤 3 1.通过private将构造器隐藏起来 4 2.把这个类的所有可能的实例都使用public static final修饰的类变量来保存 5 3.如果有必要,可以提供一些静态方法,允许其他程序根据特定的参数来获取与之匹配的实例 6 public class Season{ 7 //把Season类定义成不可变的,将其Field也定义成final 8 private final String name; 9 private final String desc

JAVA笔记2__类/封闭性/构造方法/方法的重载/匿名对象

public class Main { public static void main(String[] args) { Chicken c1 = new Chicken(); Chicken c2 = null; c1.eat(); System.out.println(c1.age); c2 = c1; c2.eat(); } } class Chicken{ int color; char sex; int age; void eat(){ System.out.println("chik

JAVA笔记11__File类/File类作业

/** * File类:文件的创建.删除.重命名.得到路径.创建时间等,是唯一与文件本身有关的操作类 */ public class Main { public static void main(String[] args) { //File.separator 表示分隔符 File f1 = new File("c:" + File.separator + "fuck" + File.separator + "javaTest1.txt");