Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/387.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 已同步,线程未按预期工作_Java_Multithreading_Synchronized_Thread Sleep - Fatal编程技术网

Java 已同步,线程未按预期工作

Java 已同步,线程未按预期工作,java,multithreading,synchronized,thread-sleep,Java,Multithreading,Synchronized,Thread Sleep,我正在尝试这样做: 有一个类Q,它有一个名为n的字段和两个方法put()和get(),用于设置n的值或检索n的值。然后有两类生产者和消费者Producer类有一个调用put的线程,consumer类有一个调用get的线程。我试图使用对象锁来同步它,这是Singleton类LockClass的唯一实例。 这是我的课堂Q: public class Q { int n; public void put(int n){ System.out.println("Put

我正在尝试这样做:

有一个类
Q
,它有一个名为
n
的字段和两个方法
put()
get()
,用于设置n的值或检索n的值。然后有两类
生产者
消费者
Producer
类有一个调用
put
的线程,
consumer
类有一个调用
get
的线程。我试图使用
对象锁
来同步它,这是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;
    }
}
锁类:

 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
所以,我实际上是在生产者和消费者中使线程休眠,以便有足够的时间进行上下文切换。现在,当两人需要同时睡觉时,若制作人先睡觉,它应该先醒来。但正如我在输出中所看到的,生产者首先睡眠,但消费者仍然首先醒来,无论哪个线程首先醒来,都应该得到锁,因此另一个线程应该等待锁被释放


为什么它没有按我预期的方式工作?我遗漏了什么吗?

睡眠超时不能保证是严格的,因此一个线程可以睡得晚一点,醒得早一点是绝对有效的。引用Thread.sleep java文档:

[…]取决于系统计时器和调度程序的精度和准确性

理论上,一个线程甚至可以执行两次操作,而第二个线程将处于休眠状态

如果希望两个线程依次执行操作,请使用等待通知机制:

制作人

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

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

睡眠超时不能保证是严格的,因此一个线程可以睡得晚,醒得早是绝对有效的。引用Thread.sleep java文档:

[…]取决于系统计时器和调度程序的精度和准确性

理论上,一个线程甚至可以执行两次操作,而第二个线程将处于休眠状态

如果希望两个线程依次执行操作,请使用等待通知机制:

制作人

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

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();    
}
从文档:

。。。然而,这些睡眠时间不能保证精确,因为 它们受到底层操作系统提供的设施的限制在任何情况下,都不能假设调用sleep 将在指定的时间段内暂停线程

来自文档:

。。。然而,这些睡眠时间不能保证精确,因为 它们受到底层操作系统提供的设施的限制在任何情况下,都不能假设调用sleep 将在指定的时间段内暂停线程


因此,我实际上是在尝试使用相同的功能,使用单锁对象,而无需等待、通知和通知所有对象。因此,如果两个线程同时睡眠,并且假设线程1睡眠时间早于线程2,那么与此同时,线程2将进入睡眠状态(假设它在x ms后睡眠)。现在,当thread1将唤醒时,JVM将控制thread1,因为没有其他线程。同时,线程2将唤醒,并应等待,因为锁已被线程1占用。这应该继续下去。。我的理解错了吗?@priyansgoel如果你不想使用sleep,试着在两个线程之间留出一个间隔,例如,在第一个线程-1之后500毫秒启动第二个线程,邀请新手以一种不应该使用的方式使用wait()/notify()/notifyAll()机制。等待/通知是该站点上noobs的一个主要困惑源。它是一个低级原语,用于实现高级同步对象。大多数问题(包括这个问题)都可以使用标准库提供的更高级别对象来解决。(例如,
BlockingQueue
在本例中可以很好地工作。)因此,我实际上尝试使用相同的功能,即使用单个lock对象,而不使用wait、notify和notifyall。因此,如果两个线程同时睡眠,并且假设线程1睡眠时间早于线程2,那么与此同时,线程2将进入睡眠状态(假设它在x ms后睡眠)。现在,当thread1将唤醒时,JVM将控制thread1,因为没有其他线程。同时,线程2将唤醒,并应等待,因为锁已被线程1占用。这应该继续下去。。我的理解错了吗?@priyansgoel如果你不想使用sleep,试着在两个线程之间留出一个间隔,例如,在第一个线程-1之后500毫秒启动第二个线程,邀请新手以一种不应该使用的方式使用wait()/notify()/notifyAll()机制。等待/通知是该站点上noobs的一个主要困惑源。它是一个低级原语,用于实现高级同步对象。大多数问题(包括这个问题)都可以使用标准库提供的更高级别对象来解决。(例如,
BlockingQueue
在这种情况下可以很好地工作。)每当你听到“生产者”和“消费者”,你首先应该想到的是
BlockingQueue
。生产者循环将对象放入队列(在本例中,对象可以是
Integer
对象),消费者循环将对象取出。如果使用者尝试移除对象时队列为空,则使用者将被阻止,直到对象可用为止。而且,如果队列有一个上限(通常是个好主意),那么每当队列满时,制作人就会被阻塞。@jameslarge:非常感谢!:)每当你听到“制作人”和“