简体   繁体   中英

Thread can't read instances that are being modified by other threads

This scheme shows the functions that are being discussed below... 正在讨论并发问题。

The OrderBook class looks like this:

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 class and Client 2 class are different from each other (they perform different write operations over the their OrderBook class) and they are launched from different threads. A Client class looks like this:

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

The problem comes when in the Main class (launched in another thread), I try to read the updated instances of the class OrderBook that are being modified by the Client x classes...

Main class looks like this...

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

QUESTIONS:

  1. How could I access consistently the last updated values of both instances of OrderBook ?
  2. Also, is it possible to show priority on write operations over read operations?

Your resource should be protected by a Lock.
Your synchronized block is useless, you should protect OrderBook methods.

synchronized keyword is used to protect resources.

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

Orderbook methods should be synchronized

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

Warning: When constructing an object that will be shared between threads, be very careful that a reference to the object does not "leak" prematurely. For example, suppose you want to maintain a List called instances containing every instance of class. You might be tempted to add the following line to your constructor: instances.add(this); But then other threads can use instances to access the object before construction of the object is complete.

Your OrderBook getter getLasValue is leaking

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM