繁体   English   中英

如何解决此内存一致性错误?

[英]How do I solve this memory consistency error?

我有一个多线程Java程序,它正在获得内存一致性错误。 我天真地试图通过声明我的所有变量volatile和方法同步来解决问题,但这没有奏效。

有时,上一个对象的内存会显示在其他新对象中。 这是现实世界的输出:

<Thread-13> OptionRow (AMGN_062714P116) expDate set to 2014-06-27
Thread-9 2) OFT-Thread (AIG_052314C50) populateTable expDate = 2014-06-27 year=2014 month=5 day=23
<Thread-9> OptionRow (AIG_052314C50) expDate set to 2014-05-23

您可以看到Thread 9将expDate设置为Thread 13设置该值的相同值。 这是错误的,不知何故,正确的价值赶上了。 很难将它作为一个小代码片段,所以我会尽量准确地描述代码序列,同时保持简短。

编辑:我修改了每个请求的代码(原始代码太短)

这段代码中仍然缺少一些东西。 我不能包含它,因为它允许您连接到TDAmeritrade的API服务器。 我可以创建随机生成的日期,如果这会更好? 我知道这不会为你编译。 我还在学习这个网站的协议。 如果这是错误的,请告诉我。

此外,当我将其作为单个线程运行时,它工作。 没有内存错误。 所以,它绝对是多线程的。 这个版本与我最初发布的版本不同。 我希望使用ExecutorService线程池会有所帮助。 它没有帮助。

import java.util.Iterator;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import broker.Root;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import networking.ParseBinaryOptionChain;

public class StackOverflowCode {
    public StackOverflowCode () {
        Root root = new Root();

        MyStatement myStatement = new MyStatement (root);
        root.setStatement(myStatement);

        networking.TDAConnection tdaConn = new networking.TDAConnection(root);
        tdaConn.login();
        root.setTDAConnection(tdaConn);

        Table table = new Table(root);
        new Thread(table).start();        
    }

    public static void main(String[] args){
        new StackOverflowCode();
    }
}

class Table implements Runnable {
    Root root;
    private ArrayList <OptionRow> optionList;
    public Table(Root r){
        // guess initial list size is 125,000 symbols
        optionList = new ArrayList<>(125000); 
        root = r;
    }

    @Override
    public void run() {
        getUnderlyingList(); 
    }

    public void addAll(ArrayList options) {
        optionList.addAll(options);
    }

    private void getUnderlyingList() {
        try {
            final TreeSet dbStocks = (TreeSet) root.getMyStatement().getStockSymbolsFromOptionsFilterDatabase();
            ExecutorService pool = Executors.newFixedThreadPool(10);
            String stock = null;
            for (Iterator it = dbStocks.iterator(); it.hasNext(); stock = (String) it.next()) {
                pool.submit(new Download(root, stock, this));
            }
            pool.shutdown();
            pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

class Download implements Runnable {

    private String stock;
    private Root root;
    private Table parent;

    public Download(Root root, String stock, Table parent) {
        this.root = root;
        this.stock = stock;
        this.parent = parent;
    }

    @Override
    public void run() {
        PopulateTable pop = new PopulateTable(root, stock, parent);
        pop.populateTable();
    }
}

class PopulateTable {
    private String stockSymbol;
    private Root root;
    private Table parent;
    private ArrayList <OptionRow> optionList;
    private ParseBinaryOptionChain pBOC;
    private volatile OptionRow option;
    private volatile GregorianCalendar expDate;

    public PopulateTable(Root root, String stockSymbol, Table parent) {
        this.root = root;
        this.stockSymbol = stockSymbol;
        this.parent = parent;
        optionList = new ArrayList<>();
    }

    public synchronized void populateTable() {
        try {
            // Download all the options associated with the stock symbol
            // and parse them directly from the Internet.
            pBOC = root.getTDAConnection().requestBinaryOptionChain(stockSymbol);

            root.getMyStatement().setLastStockPrice(stockSymbol, pBOC.getsLast());
            root.getMyStatement().setCompanyName(stockSymbol, new networking.YahooConnect(root).symbolLookup(stockSymbol));

            // Here's where we will be going through each option. Collect the info
            // we are interested in here. Put the info in the options table. 
            while (pBOC.next()&&root.buttonIsOnline()){ 

                boolean isCall = false;
                if (pBOC.getPutCallIndicator()=='C') 
                    isCall = true;

                String date = pBOC.getODate();
                expDate = new GregorianCalendar (
                        Integer.parseInt(date.substring(0, 4)),
                        (Integer.parseInt(date.substring(4, 6))-1),
                        Integer.parseInt(date.substring(6, 8)));

                System.out.println ("<"+Thread.currentThread().getName()+"> ("+pBOC.getOSymbol()+") populateTable expDate = "
                        +root.getHelp().getSdfYearMonthDay().format(expDate.getTime())
                        +" year="+Integer.parseInt(date.substring(0, 4))
                        +" month="+Integer.parseInt(date.substring(4, 6))
                        +" day="+ Integer.parseInt(date.substring(6, 8)));

                System.out.println (Thread.currentThread().getName()+" 2) OFT-Thread ("+pBOC.getOSymbol()+") populateTable expDate = "
                        +root.getHelp().getSdfYearMonthDay().format(expDate.getTime())
                        +" year="+Integer.parseInt(date.substring(0, 4))
                        +" month="+Integer.parseInt(date.substring(4, 6))
                        +" day="+ Integer.parseInt(date.substring(6, 8)));

                option = new OptionRow(root, expDate, new GregorianCalendar());
                optionList.add(option);
            }
            // add all the options to the parent table
            parent.addAll(optionList);
            // Saves all the downloaded info to the database
            root.getMyStatement().insertOptionsInfoForFilter(optionList);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

class OptionRow {
   private Root root;
   private volatile GregorianCalendar expDate;

   public OptionRow (Root root, GregorianCalendar expDate, Object... o) {
       this.root = root;
       this.expDate = expDate;
   }
}

如果有人可以告诉我为什么会发生错误以及如何解决错误,我真的很喜欢它吗?

我认为问题只是Root.getHelp().getSdfYearMonthDay()返回一个共享的日期格式化程序。 这些对象不是线程安全的,通常,共享这些对象会导致格式化另一个线程的日期。

代替

root.getHelp().getSdfYearMonthDay()

用。。。来代替:

new SimpleDateFormat("yyyy-MM-dd")

并查看问题是否仍然存在。 我敢打赌它已经消失了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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