qq_35811265 2018-05-04 23:37 采纳率: 100%
浏览 840
已采纳

Java编程思想21章的同步代码块问题

最近在看Java编程思想,遇到一个问题:
//: concurrency/CriticalSection.java
// Synchronizing blocks instead of entire methods. Also
// demonstrates protection of a non-thread-safe class
// with a thread-safe one.
package concurrency;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.*;

class Pair { // Not thread-safe
private int x, y;
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
public Pair() { this(0, 0); }
public int getX() { return x; }
public int getY() { return y; }
public void incrementX() { x++; }
public void incrementY() { y++; }
public String toString() {
return "x: " + x + ", y: " + y;
}
public class PairValuesNotEqualException
extends RuntimeException {
public PairValuesNotEqualException() {
super("Pair values not equal: " + Pair.this);
}
}
// Arbitrary invariant -- both variables must be equal:
public void checkState() {
if(x != y)
throw new PairValuesNotEqualException();
}
}

// Protect a Pair inside a thread-safe class:
abstract class PairManager {
AtomicInteger checkCounter = new AtomicInteger(0);
protected Pair p = new Pair();
private List storage =
Collections.synchronizedList(new ArrayList());
public synchronized Pair getPair() {
// Make a copy to keep the original safe:
return new Pair(p.getX(), p.getY());
}
// Assume this is a time consuming operation
protected void store(Pair p) {
storage.add(p);
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch(InterruptedException ignore) {}
}
public abstract void increment();
}

// Synchronize the entire method:
class PairManager1 extends PairManager {
public synchronized void increment() {
p.incrementX();
p.incrementY();
store(getPair());
}
}

// Use a critical section:
class PairManager2 extends PairManager {
public void increment() {
Pair temp;
synchronized(this) {
p.incrementX();
p.incrementY();
temp = getPair();
}
store(temp);
}
}

class PairManipulator implements Runnable {
private PairManager pm;
public PairManipulator(PairManager pm) {
this.pm = pm;
}
public void run() {
while(true)
pm.increment();
}
public String toString() {
return "Pair: " + pm.getPair() +
" checkCounter = " + pm.checkCounter.get();
}
}

class PairChecker implements Runnable {
private PairManager pm;
public PairChecker(PairManager pm) {
this.pm = pm;
}
public void run() {
while(true) {
pm.checkCounter.incrementAndGet();
pm.getPair().checkState();
}
}
}

public class CriticalSection {
// Test the two different approaches:
static void
testApproaches(PairManager pman1, PairManager pman2) {
ExecutorService exec = Executors.newCachedThreadPool();
PairManipulator
pm1 = new PairManipulator(pman1),
pm2 = new PairManipulator(pman2);
PairChecker
pcheck1 = new PairChecker(pman1),
pcheck2 = new PairChecker(pman2);
exec.execute(pm1);
exec.execute(pm2);
exec.execute(pcheck1);
exec.execute(pcheck2);
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch(InterruptedException e) {
System.out.println("Sleep interrupted");
}
System.out.println("pm1: " + pm1 + "\npm2: " + pm2);
System.exit(0);
}
public static void main(String[] args) {
PairManager
pman1 = new PairManager1(),
pman2 = new PairManager2();
testApproaches(pman1, pman2);
}
} /* Output: (Sample)
pm1: Pair: x: 15, y: 15 checkCounter = 272565
pm2: Pair: x: 16, y: 16 checkCounter = 3956974
*///:~

这是其并发部分的一个同步示例,这个没问题,但另外一种用Lock的就有问题了
//: concurrency/ExplicitCriticalSection.java
// Using explicit Lock objects to create critical sections.
package concurrency;
import java.util.concurrent.locks.*;

// Synchronize the entire method:
class ExplicitPairManager1 extends PairManager {
private Lock lock = new ReentrantLock();
public synchronized void increment() {
lock.lock();
try {
p.incrementX();
p.incrementY();
store(getPair());
} finally {
lock.unlock();
}
}
}

// Use a critical section:
class ExplicitPairManager2 extends PairManager {
private Lock lock = new ReentrantLock();
public void increment() {
Pair temp;
lock.lock();
try {
p.incrementX();
p.incrementY();
temp = getPair();
} finally {
lock.unlock();
}
store(temp);
}
}

public class ExplicitCriticalSection {
public static void main(String[] args) throws Exception {
PairManager
pman1 = new ExplicitPairManager1(),
pman2 = new ExplicitPairManager2();
CriticalSection.testApproaches(pman1, pman2);
}
} /* Output: (Sample)
pm1: Pair: x: 15, y: 15 checkCounter = 174035
pm2: Pair: x: 16, y: 16 checkCounter = 2608588
*///:~

这样就在checkState的时候出问题了,在pman2的检查会抛出之前定义的异常,在增的过程中两个值不相等的时候作了check,貌似ReentrantLock没起作用,不知道是什么原因

  • 写回答

3条回答 默认 最新

  • NullPoint99999 2018-05-07 08:22
    关注

    用lock实现同步是没有问题的,你第二个程序出问题是因为锁不同的原因。你第一个程序有两个锁,而你第二个程序有四个锁,getPair方法是同步方法。第一个例子中,程序执行了x++之后,如果发生了线程状态的变化,就会运行PairChecker,调用getPair时进不去,也就不会发生什么问题;而第二个例子中increment()锁是lock,发生线程状态变化,运行PairChecker,调用getPair时可以进去。 刚开始我回答了,可是之后又不见了。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
  • threenewbee 2018-05-05 11:47
    关注

    private Lock lock = new ReentrantLock();
    这里lock是实例级的,因此对于多个对象实例,只能同一个对象实例的锁定有用,它们各自拥有不同的lock对象,起不到锁定的作用。
    需要定义成static的。而且如果是跨继承的,要放在基类中。

    评论
  • HelloNW 2019-04-05 12:05
    关注

    要使x==y,则getPair()方法必须与increment()方法先后执行。这里出现问题的原因在于,使用synchronized关键字产生的锁与new ReentrantLock()的锁并不是同一个锁,getPair()方法可能在increment()运行中间执行,不能使这两个方法先后执行。可以将ExplicitPairManager1和ExplicitPairManager2中的private Lock lock = new ReentrantLock();语句删去,在抽象类PairManager中进行如下修改:

    protected Lock lock = new ReentrantLock();  //添加此语句
    //修改getPair()方法
    
    public Pair getPair() {
    
          lock.lock();
            try {
                return new Pair(p.getX(), p.getY());
            } finally {
              lock.unlock();
            }
    
      }
    
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 kali显示no x11 display variable was set;
  • ¥15 Erasure Code纠删码表
  • ¥15 用vite创建的vue3项目,404重定向不起作用??
  • ¥15 关于#c语言#的问题:一个球从80米高度自由落下,每次落地后反弹的高度为原高度的一半计算6次小球反弹的高度.(反弹结果取整,使用走走for循环结构)
  • ¥15 SurfaceControl的screenshot问题
  • ¥15 基于51单片机的oled菜单代码,要C语言,模块化编程!
  • ¥15 JAVAswing,设计一个扑克牌什么的
  • ¥50 python ctypes调用dll实现分析
  • ¥40 用python解决数据统计问题
  • ¥100 是否有方案能通过抓包分析得到移动应用的名称和包名信息?