On 06/22/2013 08:06 AM, Dmitry Nadezhin wrote: > Alexey, > > Each possible radix has its cacheLine in the cache. > > Cache contents looks like > BigInteger[][] powerCache = new BigInteger[37] { > /*0*/ null, > /*1*/ null, > /*2*/ new BigInteger[] { 2, 4, 16, 256, 32768, ... }, > /*3*/ new BigInteger[] { 3, 9, 81, ... }, > /*4*/ new BigInteger[] { 4, 16, 256, ... } > /*5*/ new BigInteger[] { 5, 25, 625, ... }, > /*6*/ new BigInteger[] { 6 }, > /*7*/ new BigInteger[] { 7 }, > . . . > /*36*/ new BigInteger[] { 36 } > }; > > Is there an idiom for a list/array of volatile references ?
AtomicReferenceArray is your friend there. Although I'm not sure why you need the list of volatile references in this case. Placing volatile to the root reference resolves the race. > I am not sure that such naive code works: > volatile BigInteger[][] powerCache = .., Why wouldn't it work? volatile T[][] cache; T[] get(int index) { T[][] lc = cache; if (index >= lc.length) { // need resizing lc = generateNew(index << 1); cache = lc; } return lc[index]; } If you need to populate the 2nd level, then you have to have the final volatile write to the $cache. The corresponding $cache volatile read makes the update on 2nd level visible. T get(int index1, index2) { T[][] lc = cache; if (index1 >= lc.length) { // needs resizing lc = generateNewT2(index1 << 1); cache = lc; } T[] lt = lc[index2]; if (index2 >= lt.length) { // needs resizing lt = generateNewT1(index2 << 1); lc[index2] = lt; cache = lc; // publish } return lt[index2]; } -Aleksey.