易失性和线程安全性

时间:2015-11-22 18:53:29

标签: java volatile

在以下代码中,

public class Mainly {
    private Map<Integer, String> map = new HashMap<Integer, String>();
    private volatile int index = 0;

    public void set(Integer key, String value) {
        map.put(key, value);         // (1)
        index ++;                    // (2)
    }

    public String get(Integer key) {
        int i = index;               // (3)
        return map.get(key);         // (4)
    }
}

如果(1)发生在(4)之前,是否意味着get方法始终会读取最新值?

3 个答案:

答案 0 :(得分:2)

好问题, 在单线程应用程序中,您将无法识别volatile变量的工作,现在让我们看看它是如何工作的。

Java volatile关键字用于将Java变量标记为&#34;存储在主存储器中#34;。更准确地说,这意味着,每次读取一个易失性变量都将从计算机的主存储器读取,而不是从CPU缓存读取,并且每次写入一个易失性变量都将被写入主存储器,而不仅仅是到CPU缓存。

想象一下两个或多个线程可以访问共享对象的情况,该共享对象包含一个声明如下的计数器变量:

public class SharedObject {

public int counter = 0;

}

想象一下,只有线程1递增计数器变量,但线程1和线程2都可能不时读取计数器变量。

如果计数器变量未声明为volatile,则无法保证何时将计数器变量的值从CPU缓存写回主存储器。这意味着CPU缓存中的计数器变量值可能与主存储器中的计数器变量值不同。

线程没有看到变量的最新值的问题,因为它还没有被另一个线程写回主内存,被称为&#34; visibility&#34;问题。其他线程看不到一个线程的更新。

通过声明计数器变量volatile,对计数器变量的所有写操作都将立即写回主存储器。此外,计数器变量的所有读取都将直接从主存储器中读取。以下是计数器变量的volatile声明的外观:

public class SharedObject {

public volatile int counter = 0;

}

声明变量volatile因此保证了对该变量的其他写入线程的可见性。

此处提供有关volatile关键字的更详细说明: http://tutorials.jenkov.com/java-concurrency/volatile.html

如果您对挥发性产品仍存在一些困惑,请告诉我。

如果得到我的观点,请点击核查符号接受答案。

快乐的编码!!民间

答案 1 :(得分:2)

您似乎正在努力使您的读/写操作线程安全。

你这样做的方式是错误的,首先关闭的是你的Mainly类用作单身人士吗?

如果是,则应同步<!DOCTYPE html> <html> <head> <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script> <style> .canvasBackground { background-color: white } .table { border-collapse: collapse; border: #d0d4d5 solid 1px; border-spacing: 0px; font: normal 11px Georgia, 'Times New Roman', Times, serif; letter-spacing: 1px; line-height: 14px; padding: 5px; width: 100% } .headerStyle { vertical-align: middle; } .headerRowStyle { background-color: #fff; border-bottom: 3px solid #ccc; color: #4078a9; font-size: 14px; height: 48px; line-height: 14px; padding: 10px 5px 5px 5px } .headerCellStyle { border-left: 1px solid #d0d4d5; } .tableBodyStyle { text-align: left; vertical-align: middle } .tableRowStyle { background-color: #fff; border-bottom: 1px solid #d0d4d5; color: #565656; padding: 5px 5px } .tableCellStyle { border: 1px solid #d0d4d5; } </style> </head> <body> <div class="canvasBackground"> <div class="tables"></div> </div> <script> function evalColor(d) { if (d == "Green" | d == "Yellow" | d == "Red") { return createSVG(d); } if (d != "Green" | d != "Yellow" | d != "Red") { return d; } } function evalText(d) { if (d == "Green" | d == "Yellow" | d == "Red") { console.log(d); } else if (d != "Green" | d != "Yellow" | d != "Red") { return d; } } function createTable() { var dataSet = [{ "Title": "Title 1", "ID": "00001", "Name": "Rena", "Color 1": "Yellow", "Color 2": "Green" }, { "Title": "Title 2", "ID": "00002", "Name": "Elsa", "Color 1": "Green", "Color 2": "Red" }, ]; var div = d3.select('.tables'); // append a table to the div var table = div.append("table") .attr({ id: "sample", class: 'table' }) .classed("display", true); // append a header to the table var thead = table.append("thead") .attr({ class: 'headerStyle' }); // append a body to the table var tbody = table.append("tbody") .attr({ class: 'tableBodyStyle' }); // append a row to the header var theadRow = thead.append("tr") .attr({ class: 'headerRowStyle' }); // return a selection of cell elements in the header row // attribute (join) data to the selection // update (enter) the selection with nodes that have data // append the cell elements to the header row // return the text string for each item in the data array theadRow.selectAll("th") .data(d3.keys(dataSet[0])) .enter() .append("th") .text(function(d) { return d; }); // table body rows var tableBodyRows = tbody.selectAll("tr") .data(dataSet) .enter() .append("tr") .attr({ class: 'tableRowStyle' }); //table body row cells tableBodyRows.selectAll("td") .data(function(d) { return d3.values(d); }) .enter() .append("td") .text(function(d) { return evalText(d); }) .filter(function(d){ return (d === "Green" || d === "Yellow" || d === "Red"); }) .append(function(d) { return createSVG(d); }); } function createSVG(d) { function colorPicker(value) { if (value == "Green") { return "#7aa25c"; } else if (value == "Yellow") { return "#f4f85e"; } else if (value == "Red") { return "#d84b2a"; } } function colorFill(value) { if (value == "Green") { return "#fff"; } else if (value == "Yellow") { return "#565656"; } else if (value == "Red") { return "#fff"; } } function letterChoice(value) { if (value == "Green") { return "G"; } else if (value == "Yellow") { return "Y"; } else if (value == "Red") { return "R"; } } var w = 50; var h = 50; var kpi = document.createElement("div"); var svg = d3.select(kpi).append("svg") .attr({ width: w, height: h }); var elem = svg.selectAll("div") .data([d]); var elemEnter = elem.enter() .append("g"); elemEnter.append("circle") .attr({ cx: 28, cy: 25, r: 20 }) .style("fill", colorPicker); elemEnter.append("text") .style("fill", colorFill) .attr("dy", 30) .attr("dx", 25) .text(letterChoice); return kpi; } createTable(); </script> </body> </html>set方法,例如:

get

这样,如果您的字段被正确封装,则所有线程将同时看到public synchronized void set(Integer key, String value) { map.put(key, value); index ++; } public synchronized String get(Integer key) { int i = index; return map.get(key); } map的相同值。

这里不需要使用index关键字,因为前缀/后缀增量/减量在Java中不是原子的。在您的情况下,仅仅确保同步是不够的。

答案 2 :(得分:0)

  

我有一个全局Map,只有一个线程更新它,并且有几个线程读取   map中的值。我能以这种方式读取地图中的最新值吗?

ConcurrentHashMap可能正是您要找的。来自API:

  

检索反映了最近完成的更新的结果   在他们开始时坚持的行动。

但是仍然无法保证在读取之前启动的更新将完成。您的返回可能有点陈旧,但至少它将保证是最新的有效值,而不是一些被修复的过程中被修改的状态。

在回答原始问题时,不,在索引变量上使用volatile对此没有帮助。其他人已经对此作出了很好的解释。两个主要选项是对读取和写入使用线程限制,或使用同步。