英文:
Consumer Producer
问题
ProdCom.java (驱动类)
import static java.lang.System.out;
public class ProdCom {
static int full = 50;
static int mutx = 0;
static int empty = 0;
static int currentSize = 0;
public static void acquire() {
while (mutx == 1);
mutx++;
}
public static void release() {
mutx--;
}
public static void main(String args[]) {
Thread t = new Thread(new Producerr());
Thread t1 = new Thread(new Consumerr());
t.start();
t1.start();
}
}
Producerr.java
class Producerr implements Runnable {
public void wwait() {
while (ProdCom.currentSize >= ProdCom.full) {
}
}
public void signal() {
ProdCom.currentSize++;
}
public void run() {
do {
this.wwait();
ProdCom.acquire();
out.println("Num elements" + ProdCom.currentSize);
out.println("producing!");
ProdCom.release();
this.signal();
} while (true);
}
}
Consumerr.java
class Consumerr implements Runnable {
public void wwait() {
while (ProdCom.currentSize <= 0) {
out.println("inside consumer wait: ");
out.println("number of elements: " + ProdCom.currentSize);
}
}
public void signal() {
ProdCom.currentSize--;
}
public void run() {
do {
this.wwait();
ProdCom.acquire();
out.println("Num elements" + ProdCom.currentSize);
out.println("Consuming!");
ProdCom.release();
this.signal();
} while (true);
}
}
以上是我对生产者消费者问题的解决方案。驱动类 ProdCom
具有变量 full
、empty
和 mutx
,用于控制生产者 t
和消费者 t1
对变量 currentSize
的访问(从而模拟缓冲区中当前项目的数量)。但是当我运行代码时,输出似乎表明 t1
和 t
并没有轮流改变 currentSize
,而是其中一个永远重复并被卡住了……我想知道为什么?谢谢。
英文:
ProdCom.java (driver class)
import static java.lang.System.out;
public class ProdCom{
static int full = 50;
static int mutx = 0;
static int empty = 0;
static int currentSize = 0;
public static void acquire(){
while (mutx == 1);
mutx++;
}
public static void release(){
mutx--;
}
public static void main(String args[]){
Thread t = new Thread(new Producerr());
Thread t1 = new Thread(new Consumerr());
t.start();
t1.start();
}
}
Producerr.java
class Producerr implements Runnable{
public void wwait(){
while (ProdCom.currentSize >= ProdCom.full){
}
} public void signal(){
ProdCom.currentSize++;
}
public void run(){
do{
this.wwait();
ProdCom.acquire();
out.println("Num elements" + ProdCom.currentSize);
out.println("producing!");
ProdCom.release();
this.signal();
} while (true);
}
}
Consumerr.java
class Consumerr implements Runnable{
public void wwait(){
while (ProdCom.currentSize <= 0){
out.println("inside consumer wait: ");
out.println("number of elements: " + ProdCom.currentSize);
}
} public void signal(){
ProdCom.currentSize--;
}
public void run(){
do{
this.wwait();
ProdCom.acquire();
out.println("Num elements" + ProdCom.currentSize);
out.println("Consuming!");
ProdCom.release();
this.signal();
} while (true);
}
}
Above is my solution to the consumer-producer problem. The driver class ProdCom
has variables full
, empty
and mutx
for controlling producer t
and consumer t1
's access to the variable currentSize
(Thus simulating the current number of items in a buffer). But when I run the code, the output seems to indicate t1 and t aren't taking turns to change currentSize
, instead one of them repeats forever and gets stuck...I'm wondering why? Thanks.
答案1
得分: 1
Java内存模型允许线程缓存变量的值,并且不同线程可以拥有不同的缓存。这意味着在acquire
方法中,自旋锁很容易变成一个无限循环:执行acquire
的线程可能会使用缓存的值mutx = 1
,并且永远不会从主内存中读取更新后的值:
while (mutx == 1); // 即使另一个线程更改了mutx,这也会变成无限循环
另一个问题是++
和--
运算符不是原子操作:它们会读取变量的值,修改它,然后写回。如果两个线程同时运行currentSize++
和currentSize--
,可能会丢失其中一个操作。
您可以通过使用AtomicInteger
对象及其方法来解决这些问题,而不是使用int
。例如,在ProdCom
中:
import java.util.concurrent.atomic.AtomicInteger;
public class ProdCom {
static AtomicInteger currentSize = new AtomicInteger(0);
static AtomicInteger mutx = new AtomicInteger(0);
public static void acquire() {
while (!mutx.compareAndSet(0, 1));
}
public static void release() {
mutx.set(0);
}
}
英文:
The Java memory models allows threads to cache the values of variables, and different threads to have different caches. This means that the spin lock in acquire
easily becomes an infinite loop: the thread in acquire may use the cached value mutx = 1
and never read the updated value from main memory:
while (mutx == 1); // infinite loop even if another thread changes mutx
Another problem is that the ++
and --
operators are not atomic: they read the value of the variable, modify it, and write it back. If two threads run currentSize++
and currentSize--
at the same time it is possible one of them is lost.
You can fix these problems by using an AtomicInteger
object and its methods instead of int
, for example in ProdCom:
static AtomicInteger currentSize = new AtomicInteger(0);
static AtomicInteger mutx = new AtomicInteger(0);
public static void acquire() {
while (!mutx.compareAndSet(0, 1));
}
public static void release() {
mutx.set(0);
}
答案2
得分: 1
我稍微改进了你的代码,你会注意到许多Joni提到的概念都得到了考虑。
ProdCom.java
import java.lang.*;
public class ProdCom {
static final int FULL = 50;
static final int EMPTY = 0;
static volatile int mutx = 0;
static volatile int currentSize = 0;
static Object lockObject = new Object();
public static void acquire() {
/* 由于mutx被定义为volatile,自旋锁会起作用,
但是你可能要重新考虑这种方法。有更廉价的方法来加热房间 */
while (mutx == 1);
mutx++;
}
public static boolean isEmpty() {
synchronized(lockObject) {
if (currentSize <= EMPTY) return true;
return false;
}
}
public static boolean isFull() {
synchronized(lockObject) {
if (currentSize >= FULL) return true;
return false;
}
}
public static int getCurrentSize() {
synchronized(lockObject) {
return currentSize;
}
}
public static void release() {
mutx--;
}
public static void incCurrentSize() {
synchronized(lockObject) {
currentSize++;
}
}
public static void decCurrentSize() {
synchronized(lockObject) {
currentSize--;
}
}
public static void main(String args[]) {
Thread t = new Thread(new Producerr());
Thread t1 = new Thread(new Consumerr());
t.start();
t1.start();
}
}
Consumerr.java
import java.lang.*;
class Consumerr implements Runnable {
public void wwait() {
while (ProdCom.isEmpty()) {
System.out.println("inside consumer wait:");
System.out.println("number of elements: " + ProdCom.getCurrentSize());
try {
/* 我们这里不会自旋 */
Thread.sleep(50);
} catch (Exception e) {
/* 什么也不做 */
}
}
}
public void signal() {
ProdCom.decCurrentSize();
}
public void run() {
do {
this.wwait();
ProdCom.acquire();
System.out.println("Num elements " + ProdCom.getCurrentSize());
System.out.println("Consuming!");
this.signal();
ProdCom.release();
} while (true);
}
}
Producerr.java
import java.lang.*;
class Producerr implements Runnable {
public void wwait() {
while (ProdCom.isFull()) {
try {
Thread.sleep(50);
} catch(Exception e) { /* 什么也不做 */ }
}
}
public void signal() {
ProdCom.incCurrentSize();
}
public void run() {
do {
this.wwait();
ProdCom.acquire();
System.out.println("Num elements : " + ProdCom.getCurrentSize());
System.out.println("producing!");
this.signal();
ProdCom.release();
} while (true);
}
}
英文:
I've improved your code a bit, and you'll notice that many of the concepts mentioned by Joni are considered.
ProdCom.java
import java.lang.*;
public class ProdCom{
static final int FULL = 50;
static final int EMPTY = 0;
static volatile int mutx = 0;
static volatile int currentSize = 0;
static Object lockObject = new Object();
public static void acquire(){
/* since mutx is defined volatile, the spinlock works,
but you reconsider this approach. There are cheaper
methods of heating the room */
while (mutx == 1);
mutx++;
}
public static boolean isEmpty() {
synchronized(lockObject) {
if (currentSize <= EMPTY) return true;
return false;
}
}
public static boolean isFull() {
synchronized(lockObject) {
if (currentSize >= FULL) return true;
return false;
}
}
public static int getCurrentSize() {
synchronized(lockObject) {
return currentSize;
}
}
public static void release(){
mutx--;
}
public static void incCurrentSize()
{
synchronized(lockObject) {
currentSize++;
}
}
public static void decCurrentSize()
{
synchronized(lockObject) {
currentSize--;
}
}
public static void main(String args[]){
Thread t = new Thread(new Producerr());
Thread t1 = new Thread(new Consumerr());
t.start();
t1.start();
}
}
Consumerr.java
import java.lang.*;
class Consumerr implements Runnable {
public void wwait() {
while (ProdCom.isEmpty()){
System.out.println("inside consumer wait: ");
System.out.println("number of elements: " + ProdCom.getCurrentSize());
try {
/* we don't spinlock here */
Thread.sleep(50);
} catch (Exception e) {
/* do nothing */
}
}
}
public void signal(){
ProdCom.decCurrentSize();
}
public void run(){
do{
this.wwait();
ProdCom.acquire();
System.out.println("Num elements " + ProdCom.getCurrentSize());
System.out.println("Consuming!");
this.signal();
ProdCom.release();
} while (true);
}
}
Producerr.java
import java.lang.*;
class Producerr implements Runnable {
public void wwait(){
while (ProdCom.isFull()){
try {
Thread.sleep(50);
} catch(Exception e) { /* do nothing */ }
}
}
public void signal(){
ProdCom.incCurrentSize();
}
public void run(){
do {
this.wwait();
ProdCom.acquire();
System.out.println("Num elements : " + ProdCom.getCurrentSize());
System.out.println("producing!");
this.signal();
ProdCom.release();
} while (true);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论