同步和线程无法按预期工作

时间:2016-04-28 08:48:59

标签: java multithreading synchronized thread-sleep

我正在尝试做这样的事情:

有一个类Q,其中有一个名为n的字段和两个方法put()get(),它们设置n的值或检索n的值。然后有两个类ProducerConsumerProducer类有一个调用put的线程,consumer类有一个调用get的线程。我正在尝试使用Object lock同步它,这是Singleton类LockClass.的唯一实例

所以,这是我的班级Q:

public class Q {

    int n;

    public void put(int n){
        System.out.println("Put " + n);
        this.n = n;
    }

    public int get(){
        System.out.println("Got " + n);
        return n;
    }
}

LockClass:

 public class LockClass {

        private static  Object Lock = new Object();

        private LockClass(){

        }


        public static Object getLock(){
            return Lock;
        }
    }

消费者:

public class Consumer implements Runnable {

    Thread t;
    Q q;


    public Consumer(Q q){
        this.q = q;
        t = new Thread(this);
        t.start();
    }
    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true){

            synchronized(LockClass.getLock()){
                q.get();
            }
            try {
                System.out.println("Consumer slept");
                Thread.sleep(1000);
                System.out.println("Consumer awake");

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

制片:

public class Producer implements Runnable {

    Q q;
    Thread t;
    int i;

    public Producer(Q q){
        this.q = q;
        t = new Thread(this);
        t.start();
        i = 0;
    }
    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true){
            i++;
            synchronized(LockClass.getLock()){
                q.put(i);
            }
            try {
                System.out.println("Producer slept");
                Thread.sleep(1000);
                System.out.println("Producer awake");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }


}

DemoClass:这个类有主函数

public class DemoClass {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Q q = new Q();

        Producer prod = new Producer(q);
        Consumer cons = new Consumer(q);
    }

}

所以,当我运行上面的代码时,我会得到这样的结果:

Put 1
Producer slept
Got 1
Consumer slept
Consumer awake
Producer awake
Got 1
Consumer slept
Put 2
Producer slept
Consumer awake
Producer awake
Got 2
Consumer slept
Put 3
Producer slept
Consumer awake
Producer awake
Got 3
Consumer slept
Put 4
Producer slept

所以,我实际上是在生产者和消费者中使线程休眠,以便有足够的时间进行上下文切换。现在当两个人都需要同时睡觉时,如果生产者先睡觉,它应该首先醒来。但是正如我在输出中看到的那样,生产者先睡觉但仍然消费者首先醒来,并且首先唤醒哪个线程应该获得锁定,因此另一个线程应该等到锁定被释放。

为什么它不像我期待的那样工作?我错过了什么吗?

2 个答案:

答案 0 :(得分:2)

来自doc:

  

......然而,这些睡眠时间并不能保证精确,因为   它们受底层操作系统提供的设施限制...... 无论如何,你不能假设调用睡眠   将在指定的时间段内暂停该主题

https://docs.oracle.com/javase/tutorial/essential/concurrency/sleep.html

答案 1 :(得分:1)

睡眠超时不保证是严格的,因此一个线程可以稍后睡眠并提前唤醒是绝对有效的。来自Thread.sleep java doc的报价:

  

[...]受制于系统定时器和调度程序的精确性和准确性

理论上,一个线程甚至可以进行两次操作,而第二个线程将会休眠。

如果您希望两个线程一个接一个地执行,请使用wait-notify机制:

<强>生产者

Object lock = LockClass.getLock();
synchronized(lock){
    q.put(i);
    lock.notifyAll();
    lock.wait();    
}

<强>消费者

Object lock = LockClass.getLock();
synchronized(lock){
    q.get(i);
    lock.notifyAll();
    lock.wait();    
}