关于Object类中的线程方法:
Object类是所有Java类的 父类,在该类中定义了三个与线程操作有关的方法,使得所有的Java类在创建之后就支持多线程
这三个方法是:notify(),notifyAll(),wait(),这几个方法都是用来控制线程的运行状态的。
方法列表如下:
notify() : 唤醒在此对象监视器上等待的单个线程 notifyAll() : 唤醒在此对象监视器上等待的所有线程 wait() : 在其他线程时调用此对象的notify()或者notifyAll()方法前,导致当前线程等待 wait(long timeout) : 在notify()或者notifyAll()方法被调用之前或者超过指定的时间之前,导致当前线程等待 wait(long timeout,int nanos) : 在notify()或者notifyAll()方法被调用之前或者超过指定的时间之前, 或者其他线程中断当前线程之前,导致当前线程等待。 --如果朋友您想转载本文章请注明转载地址" "谢谢-- 代码实例: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(); }}
运行结果:
当我们看到这个结果时一定很惊讶,我们应该记得我们仅仅是设定了程序执行两次进餐行为,但是,这里却有三轮“进餐结果”,这又是为什么呢?
其实主要是因为那五位哲学家在第一轮的进餐中,都很累了,所以当要开始第二轮进餐的时候,他们集体跑去吃精神粮食了---思考,使得整个程序进入活锁状态,
即思考的进程一直在执行当中,而吃饭的进程虽然也在运行状态,但是却一直等待有人来吃饭,直到某一位哲学家实在饿得不行从思考中醒来之后,
才真正意义上开始了第二次进餐的行为。