当前位置:编程学习 > JAVA >>

关于精确唤醒线程的问题之二

--------------------编程问答-------------------- 其实和原来的例子差不多,只不过监视的对象改变了一下,consumer监视自己本身的对象,而producer则监视需要唤醒的consumer对象,并notify唤醒它。这边的例子唤醒了指定的第5个consumer。只需要将consumer thread的实例注册保存一下就可以了。

import java.util.ArrayList;
import java.util.List;

public class NotifySpecifiedThread {

    /** the thread number of consumer **/
    private static final int consumerNumber = 10;
    /** the consumer Thread Name prefix **/
    private static final String consumerThreadName = "consumer_notify";
    /** the consumer thread index that will be notified **/
    private static final int index = 5;
    /** the list register all the consumer list **/
    private static final List<Thread> registeThreads = new ArrayList<Thread>(
            consumerNumber);

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {

        for (int i = 1; i <= consumerNumber; i++) {
            final Thread t = new ConsumerThread();
            t.setName(consumerThreadName + i);
            registeThreads.add(t);
        }

        for (final Thread t : registeThreads) {
            t.start();
        }

        new ProducerThread().join();

    }

    /**
     * Consumer synchronize the current instance itself
     * 
     * @author Administrator
     * 
     */
    static class ConsumerThread extends Thread {
        @Override
        public void run() {
            final Thread t = Thread.currentThread();
            final ThreadLocal<Boolean> isWait = new ThreadLocal<Boolean>();
            isWait.set(true);

            synchronized (this) {
                while (isWait.get()) {
                    try {
                        System.out.println("i am ready to wait,"
                                + " the thread name is:" + t.getName());
                        this.wait();
                        isWait.set(false);
                    } catch (InterruptedException ex) {
                        System.err.println(ex.getMessage());
                    }
                }

                System.out.println("i have been released, the thread name is:"
                        + t.getName());
            }
        }

    }

    /**
     * the Producer will synchronize consumer instance as thread monitor
     * 
     * @author Administrator
     * 
     */
    static class ProducerThread extends Thread {

        public ProducerThread() {
            this.start();
        }

        @Override
        public void run() {
            for (Thread consumer : registeThreads) {
                final String consumerThreadName = consumer.getName();
                if (consumerThreadName.equalsIgnoreCase(consumerThreadName
                        + index)) {
                    synchronized (consumer) {
                        consumer.notifyAll();
                    }
                }

            }
        }

    }

}
--------------------编程问答-------------------- 刚才consumerThreadName变量名字有问题。修改一下
帖子不能修改,郁闷。。

package thread;

import java.util.ArrayList;
import java.util.List;

public class NotifySpecifiedThread {

    /** the thread number of consumer **/
    private static final int consumerNumber = 10;
    /** the consumer Thread Name prefix **/
    private static final String consumerThreadName = "consumer_notify";
    /** the consumer thread index that will be notified **/
    private static final int index = 5;
    /** the list register all the consumer list **/
    private static final List<Thread> registeThreads = new ArrayList<Thread>(
            consumerNumber);

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {

        for (int i = 1; i <= consumerNumber; i++) {
            final Thread t = new ConsumerThread();
            t.setName(consumerThreadName + i);
            registeThreads.add(t);
        }

        for (final Thread t : registeThreads) {
            t.start();
        }

        new ProducerThread().join();

    }

    /**
     * Consumer synchronize the current instance itself
     * 
     * @author Administrator
     * 
     */
    static class ConsumerThread extends Thread {
        @Override
        public void run() {
            final Thread t = Thread.currentThread();
            final ThreadLocal<Boolean> isWait = new ThreadLocal<Boolean>();
            isWait.set(true);

            synchronized (this) {
                while (isWait.get()) {
                    try {
                        System.out.println("i am ready to wait,"
                                + " the thread name is:" + t.getName());
                        this.wait();
                        isWait.set(false);
                    } catch (InterruptedException ex) {
                        System.err.println(ex.getMessage());
                    }
                }

                System.out.println("i have been released, the thread name is:"
                        + t.getName());
            }
        }

    }

    /**
     * the Producer will synchronize consumer instance as thread monitor
     * 
     * @author Administrator
     * 
     */
    static class ProducerThread extends Thread {

        public ProducerThread() {
            this.start();
        }

        @Override
        public void run() {
            for (Thread consumer : registeThreads) {
                final String name = consumer.getName();
                if (name.equalsIgnoreCase(consumerThreadName + index)) {
                    synchronized (consumer) {
                        consumer.notifyAll();
                    }
                }

            }
        }

    }

}
--------------------编程问答--------------------
引用 2 楼 shnulaa 的回复:
刚才consumerThreadName变量名字有问题。修改一下
帖子不能修改,郁闷。。

package thread;

import java.util.ArrayList;
import java.util.List;

public class NotifySpecifiedThread {

    /** the thread number of consumer **/
    private static final int consumerNumber = 10;
    /** the consumer Thread Name prefix **/
    private static final String consumerThreadName = "consumer_notify";
    /** the consumer thread index that will be notified **/
    private static final int index = 5;
    /** the list register all the consumer list **/
    private static final List<Thread> registeThreads = new ArrayList<Thread>(
            consumerNumber);

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {

        for (int i = 1; i <= consumerNumber; i++) {
            final Thread t = new ConsumerThread();
            t.setName(consumerThreadName + i);
            registeThreads.add(t);
        }

        for (final Thread t : registeThreads) {
            t.start();
        }

        new ProducerThread().join();

    }

    /**
     * Consumer synchronize the current instance itself
     * 
     * @author Administrator
     * 
     */
    static class ConsumerThread extends Thread {
        @Override
        public void run() {
            final Thread t = Thread.currentThread();
            final ThreadLocal<Boolean> isWait = new ThreadLocal<Boolean>();
            isWait.set(true);

            synchronized (this) {
                while (isWait.get()) {
                    try {
                        System.out.println("i am ready to wait,"
                                + " the thread name is:" + t.getName());
                        this.wait();
                        isWait.set(false);
                    } catch (InterruptedException ex) {
                        System.err.println(ex.getMessage());
                    }
                }

                System.out.println("i have been released, the thread name is:"
                        + t.getName());
            }
        }

    }

    /**
     * the Producer will synchronize consumer instance as thread monitor
     * 
     * @author Administrator
     * 
     */
    static class ProducerThread extends Thread {

        public ProducerThread() {
            this.start();
        }

        @Override
        public void run() {
            for (Thread consumer : registeThreads) {
                final String name = consumer.getName();
                if (name.equalsIgnoreCase(consumerThreadName + index)) {
                    synchronized (consumer) {
                        consumer.notifyAll();
                    }
                }

            }
        }

    }

}



 不好意思,这里我有两个地方没看明白:
  1. 这里Consumer为什么要监视自己的变量呢? 且该变量不是共享的。 
  2. Producer监视Consumer变量,然后notifyAll(),就能唤醒指定的线程。这是啥原因呢?
--------------------编程问答-------------------- 求大神们指点啊!  --------------------编程问答-------------------- 因为每个消费者线程把自己当锁,进去把自己锁住

生产者可以看到所有这些线程,只要找到这个线程就可开锁,因为锁就是这个线程本身

这里没人监视,或者说消费者锁"监视"其实是把自己卡住

并且这里notifyall或者notify是一样的,因为每次只有一个线程在等待着把锁,
这段代码其实和你上个帖子2楼回答的一样 --------------------编程问答--------------------  

引用 5 楼 yktd26 的回复:
因为每个消费者线程把自己当锁,进去把自己锁住

生产者可以看到所有这些线程,只要找到这个线程就可开锁,因为锁就是这个线程本身

这里没人监视,或者说消费者锁"监视"其实是把自己卡住

并且这里notifyall或者notify是一样的,因为每次只有一个线程在等待着把锁,
这段代码其实和你上个帖子2楼回答的一样


 可是如果是这样的话,那就起不到共享变量释放时唤醒特定线程的目的了啊!
 每个消费者锁住自己进行wait(),然后生产者锁住指定的消费者线程,对该线程唤醒,这样的话,还是达不到开始说的,当共享变量释放时, 能够指定一个消费者进行唤醒的目的。 --------------------编程问答--------------------
引用 6 楼 cloudeagle_bupt 的回复:
 可是如果是这样的话,那就起不到共享变量释放时唤醒特定线程的目的了啊!
 每个消费者锁住自己进行wait(),然后生产者锁住指定的消费者线程,对该线程唤醒,这样的话,还是达不到开始说的,当共享变量释放时, 能够指定一个消费者进行唤醒的目的。


可能我没弄清楚你的要求,但是它上面给的例子确实是指定线程唤醒啊,他那段代码唯一可能要改的是消费者run里面不应该用isWait.get()当作while的条件,这样做会使消费者执行一次消费后就退出了

可能你想像的是 (伪代码)

object.release();
object.notify(threadID);

但事实是

object.release();
Thread.findById(threadID).notify();

因为第一种方式的第二行java里是做不到的,当所有线程共享同一把锁时无法唤醒指定线程
所以需要每个线程有自己的锁

第二种情况并不影响你在消费者线程里有以下操作比如构造时注入你的共享对象

CustomThread(Ojbect object)

并在处理这个资源时加同步

while(true){
...
this.wait();
sychronized(this.object){
....
}
}

--------------------编程问答--------------------
引用 7 楼 yktd26 的回复:
Quote: 引用 6 楼 cloudeagle_bupt 的回复:

 可是如果是这样的话,那就起不到共享变量释放时唤醒特定线程的目的了啊!
 每个消费者锁住自己进行wait(),然后生产者锁住指定的消费者线程,对该线程唤醒,这样的话,还是达不到开始说的,当共享变量释放时, 能够指定一个消费者进行唤醒的目的。


可能我没弄清楚你的要求,但是它上面给的例子确实是指定线程唤醒啊,他那段代码唯一可能要改的是消费者run里面不应该用isWait.get()当作while的条件,这样做会使消费者执行一次消费后就退出了

可能你想像的是 (伪代码)

object.release();
object.notify(threadID);

但事实是

object.release();
Thread.findById(threadID).notify();

因为第一种方式的第二行java里是做不到的,当所有线程共享同一把锁时无法唤醒指定线程
所以需要每个线程有自己的锁

第二种情况并不影响你在消费者线程里有以下操作比如构造时注入你的共享对象

CustomThread(Ojbect object)

并在处理这个资源时加同步

while(true){
...
this.wait();
sychronized(this.object){
....
}
}




明白你的意思,可是我按照这样修改之后的共享变量的代码如下,还是无法顺利唤醒:



package slot;
 
import java.util.ArrayList;
import java.util.List;
 
public class NotifySpecificThread {
 
    /** the thread number of consumer **/
    private static final int consumerNumber = 10;
    /** the consumer Thread Name prefix **/
    private static final String consumerThreadName = "consumer_notify";
    /** the consumer thread index that will be notified **/
    private static final int index = 6;
    /** the list register all the consumer list **/
    private static final List<Thread> registeThreads = new ArrayList<Thread>(
            consumerNumber);
 
    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
 
     Slot s = new Slot();
        for (int i = 1; i <= consumerNumber; i++) {
            final Thread t = new ConsumerThread(s);
            t.setName(consumerThreadName + i);
            registeThreads.add(t);
        }
 
        for (final Thread t : registeThreads) {
            t.start();
        }
 
        new ProducerThread(s).join();
 
    }
 
    /**
     * Consumer synchronize the current instance itself
     * 
     * @author Administrator
     * 
     */
    static class ConsumerThread extends Thread {
    
     Slot slot ;
    
     ConsumerThread(Slot s){
     slot = s ;
     }
    
        @Override
        public void run() {
            final Thread t = Thread.currentThread();
            final ThreadLocal<Boolean> isWait = new ThreadLocal<Boolean>();
            isWait.set(true);
 
            synchronized (this) {
                while (slot.slotNum==0) {
                    try {
                        System.out.println("i am ready to wait,"
                                + " the thread name is:" + t.getName());
                        this.wait();
                        
                    } catch (InterruptedException ex) {
                        System.err.println(ex.getMessage());
                    }
                }
                
                slot.consume();
 
                System.out.println("i have been released, the thread name is:"
                        + t.getName());
            }
        }
 
    }
 
    /**
     * the Producer will synchronize consumer instance as thread monitor
     * 
     * @author Administrator
     * 
     */
    static class ProducerThread extends Thread {
 
     Slot slot ;
    
        public ProducerThread(Slot s) {
         this.slot = s;
            this.start();
        }
 
        @Override
        public void run() {
            for (Thread consumer : registeThreads) {
                final String name = consumer.getName();
                if (name.equalsIgnoreCase(consumerThreadName + index)) {
                    synchronized (consumer) {
                        consumer.notifyAll();
                    }
                }
 
            }
        }
    }
 
}

class Slot {
public int slotNum = 0;

public synchronized void produce() {
slotNum++;
}

public synchronized void consume() {
slotNum--;
}
}

 
 这是什么原因呢? --------------------编程问答--------------------
引用 7 楼 yktd26 的回复:
Quote: 引用 6 楼 cloudeagle_bupt 的回复:

 可是如果是这样的话,那就起不到共享变量释放时唤醒特定线程的目的了啊!
 每个消费者锁住自己进行wait(),然后生产者锁住指定的消费者线程,对该线程唤醒,这样的话,还是达不到开始说的,当共享变量释放时, 能够指定一个消费者进行唤醒的目的。


可能我没弄清楚你的要求,但是它上面给的例子确实是指定线程唤醒啊,他那段代码唯一可能要改的是消费者run里面不应该用isWait.get()当作while的条件,这样做会使消费者执行一次消费后就退出了

可能你想像的是 (伪代码)

object.release();
object.notify(threadID);

但事实是

object.release();
Thread.findById(threadID).notify();

因为第一种方式的第二行java里是做不到的,当所有线程共享同一把锁时无法唤醒指定线程
所以需要每个线程有自己的锁

第二种情况并不影响你在消费者线程里有以下操作比如构造时注入你的共享对象

CustomThread(Ojbect object)

并在处理这个资源时加同步

while(true){
...
this.wait();
sychronized(this.object){
....
}
}



晕,刚才没仔细看结果,重新修改了下代码,发现6确实被唤醒了,只不过刚被唤醒又陷入wait(),看来这样确实是能对特定线程实现唤醒的!
代码如下:



package slot;
 
import java.util.ArrayList;
import java.util.List;
 
public class NotifySpecificThread {
 
    /** the thread number of consumer **/
    private static final int consumerNumber = 10;
    /** the consumer Thread Name prefix **/
    private static final String consumerThreadName = "consumer_notify";
    /** the consumer thread index that will be notified **/
    private static final int index = 6;
    /** the list register all the consumer list **/
    private static final List<Thread> registeThreads = new ArrayList<Thread>(
            consumerNumber);
 
    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
 
     Slot s = new Slot();
        for (int i = 1; i <= consumerNumber; i++) {
            final Thread t = new ConsumerThread(s);
            t.setName(consumerThreadName + i);
            registeThreads.add(t);
        }
 
        for (final Thread t : registeThreads) {
            t.start();
        }
 
        new ProducerThread(s).join();
 
    }
 
    /**
     * Consumer synchronize the current instance itself
     * 
     * @author Administrator
     * 
     */
    static class ConsumerThread extends Thread {
    
     Slot slot ;
    
     ConsumerThread(Slot s){
     slot = s ;
     }
    
        @Override
        public void run() {
            final Thread t = Thread.currentThread();
 
            synchronized (this) {
                while (slot.slotNum==0) {
                    try {
                        System.out.println("i am ready to wait,"
                                + " the thread name is:" + t.getName());
                        this.wait();
                        
                    } catch (InterruptedException ex) {
                        System.err.println(ex.getMessage());
                    }
                }
                
                slot.consume();
 
                System.out.println("i have been released, the thread name is:"
                        + t.getName());
            }
        }
 
    }
 
    /**
     * the Producer will synchronize consumer instance as thread monitor
     * 
     * @author Administrator
     * 
     */
    static class ProducerThread extends Thread {
 
     Slot slot ;
    
        public ProducerThread(Slot s) {
         this.slot = s;
            this.start();
        }
 
        @Override
        public void run() {
            for (Thread consumer : registeThreads) {
                final String name = consumer.getName();
                if (name.equalsIgnoreCase(consumerThreadName + index)) {
                    synchronized (consumer) {
                        consumer.notifyAll();
                    }
                }
 
            }
        }
    }
 
}

class Slot {
public int slotNum = 0;

public synchronized void produce() {
slotNum++;
}

public synchronized void consume() {
slotNum--;
}
}
--------------------编程问答-------------------- 运行的结果:

i am ready to wait, the thread name is:consumer_notify1
i am ready to wait, the thread name is:consumer_notify4
i am ready to wait, the thread name is:consumer_notify6
i am ready to wait, the thread name is:consumer_notify2
i am ready to wait, the thread name is:consumer_notify3
i am ready to wait, the thread name is:consumer_notify5
i am ready to wait, the thread name is:consumer_notify7
i am ready to wait, the thread name is:consumer_notify8
i am ready to wait, the thread name is:consumer_notify9
i am ready to wait, the thread name is:consumer_notify10
i am ready to wait, the thread name is:consumer_notify6

可见6确实是被精确唤醒了的!  谢谢诸位大神了!
--------------------编程问答-------------------- 确实是这样的 --------------------编程问答--------------------
引用 7 楼 yktd26 的回复:
Quote: 引用 6 楼 cloudeagle_bupt 的回复:

 可是如果是这样的话,那就起不到共享变量释放时唤醒特定线程的目的了啊!
 每个消费者锁住自己进行wait(),然后生产者锁住指定的消费者线程,对该线程唤醒,这样的话,还是达不到开始说的,当共享变量释放时, 能够指定一个消费者进行唤醒的目的。


可能我没弄清楚你的要求,但是它上面给的例子确实是指定线程唤醒啊,他那段代码唯一可能要改的是消费者run里面不应该用isWait.get()当作while的条件,这样做会使消费者执行一次消费后就退出了

可能你想像的是 (伪代码)

object.release();
object.notify(threadID);

但事实是

object.release();
Thread.findById(threadID).notify();

因为第一种方式的第二行java里是做不到的,当所有线程共享同一把锁时无法唤醒指定线程
所以需要每个线程有自己的锁

第二种情况并不影响你在消费者线程里有以下操作比如构造时注入你的共享对象

CustomThread(Ojbect object)

并在处理这个资源时加同步

while(true){
...
this.wait();
sychronized(this.object){
....
}
}



  可是这里还有个疑问,为什么可以像这种方式唤醒特定线程呢:
  Producer:
    synchronized (consumerThread) {
                        consumer.notifyAll();
                    }
  这样就能唤醒一个特定线程。

  就拿上面楼里的程序来说, consumerThread在wait()后,应该是放弃了锁并在slot的一个公共区域内等待被唤醒,此时Producer锁住该线程,并执行notifyAll()操作,为什么就能唤醒呢? slot的锁怎么办呢? --------------------编程问答-------------------- 我感觉你的疑惑可能是因为没有分清楚他给的代码里,线程,和线程这个对象的区别,为了方便起见,他给的代码将线程对象当锁,这些对象是对消费线程--他自己实现的线程,和生产线程共享的,这些消费者线程类,除了run函数里充当了线程的角色外,他们的实例只是充当了object的一个角色--锁

我说的颠三倒四的也不知道你看懂了么 --------------------编程问答--------------------
引用 13 楼 yktd26 的回复:
我感觉你的疑惑可能是因为没有分清楚他给的代码里,线程,和线程这个对象的区别,为了方便起见,他给的代码将线程对象当锁,这些对象是对消费线程--他自己实现的线程,和生产线程共享的,这些消费者线程类,除了run函数里充当了线程的角色外,他们的实例只是充当了object的一个角色--锁

我说的颠三倒四的也不知道你看懂了么


明白,这里的区别就是是用线程中的一个变量做锁还是使用整个线程对象作为锁,
补充:Java ,  Java SE
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,