Java 更新后看到相同值的两个线程
我有下面的代码,其中两个线程检查堆栈中是否有元素,如果有,则将其弹出。出于某种原因,两个线程中的一个总是会得到java.util.EmptyStack异常,这意味着它会弹出一个空堆栈。我的问题是,是否应该同步此方法以防止出现这种情况?另外,是否可以仅使用同步方法来防止我的错误,或者必须实现某种join/countdownlatch/cyclicbarier/etc?我问这个问题的原因是因为理想情况下,我希望在任何给定的时间只有一个线程能够访问堆栈板Java 更新后看到相同值的两个线程,java,multithreading,synchronization,Java,Multithreading,Synchronization,我有下面的代码,其中两个线程检查堆栈中是否有元素,如果有,则将其弹出。出于某种原因,两个线程中的一个总是会得到java.util.EmptyStack异常,这意味着它会弹出一个空堆栈。我的问题是,是否应该同步此方法以防止出现这种情况?另外,是否可以仅使用同步方法来防止我的错误,或者必须实现某种join/countdownlatch/cyclicbarier/etc?我问这个问题的原因是因为理想情况下,我希望在任何给定的时间只有一个线程能够访问堆栈板 public synchronized voi
public synchronized void checkPlate(){
//System.out.println(this.name + " checks the plate for pancake");
Boolean isEmpty = Producer.plate.isEmpty();
if ( isEmpty == false){
System.out.println(this.name + " grabs a pancake");
Producer.plate.pop();
eat();
this.eatenPancakes +=1;
}else{
try {
Thread.sleep(200);// waits one minute seconds instead of minutes
checkPlate();
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
更新在提出建议后,我决定尝试用这段代码在producer plate上实现一个同步块。这样做后,我的所有线程似乎都冻结了。这个同步块是否也需要添加到我的生产者对象/线程中
public void checkPlate(){
//System.out.println(this.name + " checks the plate for pancake");
synchronized (Producer.plate){
Boolean isEmpty = Producer.plate.isEmpty();
if ( isEmpty == false){
System.out.println(this.name + " grabs a pancake");
Producer.plate.pop();
eat();
this.eatenPancakes +=1;
}else{
try {
Thread.sleep(200);// waits since theres no pancake
checkPlate();
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
更新2提供更多信息的目标。在每个线程抓取一个煎饼后,它会使用以下方法随机吃/睡一段时间。理想情况下,整个程序的目标是模拟制作人不断烹饪煎饼的地方,并将其添加到堆栈中。两个线程中的一个检查堆栈,如果堆栈中有元素,则抓取一个煎饼。这将发生,直到生产者线程完成煎饼烹饪
public void eat(){
Random ran = new Random();
double eatingTime;
if (this.name == "Piggy"){
eatingTime = ran.nextDouble() * (4 - 2) + 2; //generates a value between 2 and 4
}else{
eatingTime = ran.nextDouble() * (5 - 3) + 3; //generates a
}
try {
System.out.println(this.name + " starts eating a pancake...");
Thread.sleep((long)eatingTime *100);//mili seconds instead of minutes
Boolean isEmpty = Producer.plate.isEmpty();
if (Producer.finished == false && isEmpty == false){
checkPlate();
}
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
我的问题是,是否应该同步此方法以防止出现这种情况
基本上,你没有给我们提供足够的信息。但如果有两个类实例包含此方法,则绝对不是。它们将具有不同的监视器,因此可以同时位于同步方法中
看起来您正在使用一个静态变量(Producer.plate
)作为共享队列-在这种情况下,实例级同步基本上不会对您有所帮助,因为可能有多个实例调用了checkPlate
不过,同步可能是不必要的。基本上,您应该使用中的一个集合-这些集合是为并发访问而设计的,非常适合生产者/消费者队列
此外,当前的代码在盘子空的时候会递归得越来越深,这不是一个好主意。如果您希望它一直执行,直到它消费了一个项目,那么应该使用while
循环而不是递归
public void checkPlate(){
while(!Producer.finished)
//System.out.println(this.name + " checks the plate for pancake");
synchronized (Producer.plate){
Boolean isEmpty = Producer.plate.isEmpty();
if ( isEmpty == false){
System.out.println(this.name + " grabs a pancake");
Producer.plate.pop();
eat();
this.eatenPancakes +=1;
}
}
try {
Thread.sleep(200);// waits since theres no pancake
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
我的问题是,是否应该同步此方法以防止出现这种情况
基本上,你没有给我们提供足够的信息。但如果有两个类实例包含此方法,则绝对不是。它们将具有不同的监视器,因此可以同时位于同步方法中
看起来您正在使用一个静态变量(Producer.plate
)作为共享队列-在这种情况下,实例级同步基本上不会对您有所帮助,因为可能有多个实例调用了checkPlate
不过,同步可能是不必要的。基本上,您应该使用中的一个集合-这些集合是为并发访问而设计的,非常适合生产者/消费者队列
此外,当前的代码在盘子空的时候会递归得越来越深,这不是一个好主意。如果您想让它一直执行直到它消耗了一个项,那么应该使用
while
循环而不是递归。不幸的是,您没有显示调用部分。最重要的是,我们必须看看这个方法是在同一个对象上调用还是在不同的对象上调用
public void checkPlate(){
while(!Producer.finished)
//System.out.println(this.name + " checks the plate for pancake");
synchronized (Producer.plate){
Boolean isEmpty = Producer.plate.isEmpty();
if ( isEmpty == false){
System.out.println(this.name + " grabs a pancake");
Producer.plate.pop();
eat();
this.eatenPancakes +=1;
}
}
try {
Thread.sleep(200);// waits since theres no pancake
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
如果它从共享同一资源(生产者)的两个不同对象调用该方法,则会出现问题
看起来事情就是这样
我还认为你还有另一个问题。如果堆栈足够大,递归将导致堆栈溢出异常
澄清后更新 从本质上讲,同步化方法与同步化块相同,监视器是对象本身:
synchronized(this) {
// code
}
因为您有两个单独的对象,所以它们的“this”不是同一个对象,并且它们实际上不会锁定您的资源
锁定资源的简单经典准则是访问必须由共享资源同步。由于您共享Producer.plate,您可以执行以下操作:
synchronized(Producer.plate) {
// code
}
其他对象也可以用作监视器,但开发人员有责任确保对其进行适当管理,以确保同步访问Product.plate
更新以提供重做方法,该方法检查生产者是否在无递归的情况下完成烹饪
public void checkPlate(){
while(!Producer.finished)
//System.out.println(this.name + " checks the plate for pancake");
synchronized (Producer.plate){
Boolean isEmpty = Producer.plate.isEmpty();
if ( isEmpty == false){
System.out.println(this.name + " grabs a pancake");
Producer.plate.pop();
eat();
this.eatenPancakes +=1;
}
}
try {
Thread.sleep(200);// waits since theres no pancake
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
不幸的是,您没有显示调用部分。最重要的是,我们必须看看这个方法是在同一个对象上调用还是在不同的对象上调用 如果它从共享同一资源(生产者)的两个不同对象调用该方法,则会出现问题 看起来事情就是这样 我还认为你还有另一个问题。如果堆栈足够大,递归将导致堆栈溢出异常
澄清后更新 从本质上讲,同步化方法与同步化块相同,监视器是对象本身:
synchronized(this) {
// code
}
因为您有两个单独的对象,所以它们的“this”不是同一个对象,并且它们实际上不会锁定您的资源
锁定资源的简单经典准则是访问必须由共享资源同步。由于您共享Producer.plate,您可以执行以下操作:
synchronized(Producer.plate) {
// code
}
其他对象也可以用作监视器,但开发人员有责任确保对其进行适当管理,以确保同步访问Product.plate
更新以提供重做方法,该方法检查生产者是否在无递归的情况下完成烹饪
public void checkPlate(){
while(!Producer.finished)
//System.out.println(this.name + " checks the plate for pancake");
synchronized (Producer.plate){
Boolean isEmpty = Producer.plate.isEmpty();
if ( isEmpty == false){
System.out.println(this.name + " grabs a pancake");
Producer.plate.pop();
eat();
this.eatenPancakes +=1;
}
}
try {
Thread.sleep(200);// waits since theres no pancake
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
在这种情况下,使用同步块会有帮助吗?与同步(生产商板)相同