线程无法读取其他线程正在修改的实例

时间:2020-08-27 12:10:54

标签: java multithreading concurrency thread-safety

此方案显示了下面要讨论的功能... Concurrency problem being discussed.

OrderBook类如下:

public class OrderBook {

    private TreeMap<Double, Double> bids;
    private TreeMap<Double, Double> asks;
    private Entry<Double, Double> bestBid;
    private Entry<Double, Double> bestAsk;

    public OrderBook() {
        this.bids = new TreeMap<>();
        this.asks = new TreeMap<>();
    }
    
    // Getters and setters...
    
    // Example function that modifies its variables...
    public void updateBids(double bidPrice, double bidVol) {
        if(this.getBids().containsKey(bidPrice)) {
            if(bidVol == 0.0) {
                //System.out.println("Vol. 0: " + bidPrice + " - " + bidVol);
                this.getBids().remove(bidPrice);
            }
            else if(bidVol > 0.0) {
                //System.out.println("Actualizar Vol.: " + bidPrice + " - " + bidVol);
                this.getBids().replace(bidPrice, bidVol);
            }
            else {
                //System.out.println("Error. Unexpected volume:" + 
                //        bidPrice + " - " + vol);
            }
        }
        else {
            // ...
        }
        this.setBestBid(this.getBids().lastEntry());
    }
}

Client 1类和Client 2类彼此不同(它们在其OrderBook类上执行不同的写操作),并且它们是从不同的线程启动的。 Client类如下:

public class Client1 extends WebSocketClient {
    
    private OrderBook ob;
    
    public Client1(URI serverURI, OrderBook ob) {
        super(serverURI);
        this.ob = ob;
    }
   
    // Extended class implementations...

    @Override
    public void onMessage(String message) {
        parse(message);
    }
    
    private void parse(String msg) {
        JSONObject json = new JSONObject(msg);
        
        if(json.has("b")) {
            double b = json.getDouble("b");
            double a = json.getDouble("a");
            double B = json.getDouble("B");
            double A = json.getDouble("A");
            
            /**
             * HERE performs the modification of the OrderBook class passed in the
             * constructor. I don't know if this synchronized block is needed...
             */
            synchronized(this.ob) {
                this.ob.setBestBid(new AbstractMap.SimpleEntry<>(bidPrice, bidVol));
                this.ob.setBestAsk(new AbstractMap.SimpleEntry<>(askPrice, askVol));
            }
        } 
    }
}

Main类中(在另一个线程中启动),当我尝试读取OrderBook类正在修改的类Client x的更新实例时,就会出现问题。 ..

Main类看起来像这样...

public class Main implements Runnable {
    
    private OrderBook ob1;
    private OrderBook ob2;
    
    public Oportunity(OrderBook ob1, OrderBook ob2) throws URISyntaxException {
        this.ob1 = ob1;
        this.ob2 = ob2;
    } 
    @Override
    public void run() {
        while(true) {
            // PROBLEM HERE: doesn't show anything...
            System.out.println(this.ob1.getLasValue());
            System.out.println(this.ob2.getLasValue());
        }   
    }

    public static void main(String[] args) throws Throwable {
    
        OrderBook ob1 = new OrderBook();
        OrderBook ob2 = new OrderBook();
    
        Thread client1 = new Thread(new Client1(new URI("..."), ob1));
        Thread client2 = new Thread(new Client2(new URI("..."), ob2));
        
        Thread m = new Thread(new Main(ob1, ob2));
    
        client1.start();
        client2.start();
    
        m.start();
    
    }
}

问题:

  1. 如何连续访问OrderBook的两个实例的最新更新值?
  2. 此外,是否有可能在写操作上优先于读操作?

1 个答案:

答案 0 :(得分:0)

您的资源应受锁保护。
您的同步块没有用,您应该保护OrderBook方法。

synchronized关键字用于保护资源。

public class CriticalData { 
    private int sum = 0;

    public void synchronized add() {
        this.setSum(this.getSum() + 1);
    }    
    // Synchronized Getters & setters
}

@Test
public void multiThreadedAddition() {
    // GIVEN
    ExecutorService service = Executors.newFixedThreadPool(3);
    CriticalData data = new CriticalData();
    // WHEN
    IntStream.range(0, 1000)
        .forEach(count -> service.submit(data::add));
    service.awaitTermination(1000, TimeUnit.MILLISECONDS); 
    // THEN
    assertEquals(1000, data.getSum());
}

订单方法应该同步

public void synchronized updateBids(double bidPrice, double bidVol) {
    // Edit critical data here
}

警告:构造一个将在线程之间共享的对象时,请非常小心,以免对该对象的引用过早“泄漏”。例如,假设您要维护一个名为instance的List,其中包含类的每个实例。您可能会想将以下行添加到构造函数中:instance.add(this); 但是然后其他线程可以使用实例访问对象,直到对象的构造完成。

您的OrderBook getter getLasValue正在泄漏

相关问题