Replace null check with Optional - java

I would like to know since i'm fan of java 14 if replacing null checks with Optional.ofNullable
is safe for this language. I know a simple null check doesn't cost any memory while new creating new objects like optional cost but i guess it has zero performance impact or memory impact. Can someone enlight me?
The code for my game was like:
if (item!= null)
{
if (item.getCrystal() == Crystal.A)
{
player.getInventory().addItem(inventoryItem);
}
}
to something which i enjoy and i find cool
Optional.ofNullable(item).filter(i -> i.getCrystal() == Crystal.A).ifPresent(k -> player.getInventory.addItem(i));
Can someone enlight me that i'm ok with it? Maybe is cool but cost a lot? I don't know.
Thanks a lot.

In simple cases like this, the just in time compiler can likely elide the object allocation, or at least allocate it on the stack, so the overhead is negligible.
Here's a small benchmark:
public abstract class Benchmark {
final String name;
public Benchmark(String name) {
this.name = name;
}
#Override
public String toString() {
return name + "\t" + time() + " ns / iteration";
}
private BigDecimal time() {
try {
// automatically detect a reasonable iteration count (and trigger just in time compilation of the code under test)
int iterations;
long duration = 0;
for (iterations = 1; iterations < 1_000_000_000 && duration < 1_000_000_000; iterations *= 2) {
long start = System.nanoTime();
run(iterations);
duration = System.nanoTime() - start;
cleanup();
}
return new BigDecimal((duration) * 1000 / iterations).movePointLeft(3);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
/**
* Executes the code under test.
* #param iterations
* number of iterations to perform
* #return any value that requires the entire code to be executed (to
* prevent dead code elimination by the just in time compiler)
* #throws Throwable
* if the test could not complete successfully
*/
protected abstract Object run(int iterations) throws Throwable;
/**
* Cleans up after a run, setting the stage for the next.
*/
protected void cleanup() {
// do nothing
}
public static void main(String[] args) throws Exception {
Integer[] a = {null, -1, null, 1}; // mix nulls and real values
System.out.println(new Benchmark("Optional") {
#Override
protected Object run(int iterations) throws Throwable {
int[] sum = {0};
for (int i = 0; i < iterations; i++) {
Optional.ofNullable(a[i & 3]).filter(k -> k > 0).ifPresent(k -> sum[0] += k);
}
return sum[0];
}
});
System.out.println(new Benchmark("if != null") {
#Override
protected Object run(int iterations) throws Throwable {
int[] sum = {0};
for (int i = 0; i < iterations; i++) {
var k = a[i & 3];
if (k != null && k % 2 != 0) {
sum[0] += k;
}
}
return sum[0];
}
});
}
}
This shows that the overhead of using an Optional is about 1 ns, i.e. a modern CPU can construct and evaluate about 1 billion Optional objects per second. In all but the most extreme and contrived of workloads, the use of Optional will not affect performance enough for humans to notice.
The decision to use Optional should therefore not be guided by performance considerations, but by which version allows yourself to express yourself more clearly and simply.
In this case, I'd argue that the if statement is actually more readable. Sure, you've squashed everything onto a single line, but you could do the same with an if statement:
if (item != null && item.getCrystal() == Crystal.A) player.getInventory().addItem(inventoryItem);
If you do that, you'll notice that the if statement is actually shorter and more to the point than your version:
Optional.ofNullable(item).filter(i -> i.getCrystal() == Crystal.A).ifPresent(k -> player.getInventory.addItem(i));
Of course, in real code you'll probably want to keep your lines reasonably short, so the comparison is
if (item != null && item.getCrystal() == Crystal.A) {
player.getInventory().addItem(inventoryItem);
}
vs
Optional.ofNullable(item).filter(i -> i.getCrystal() == Crystal.A)
.ifPresent(k -> player.getInventory.addItem(i));
Again, I find the first version more readable, because it contains fewer words.

Related

Java: good programming approach?

I am being asked to learn Java very quickly and I am struggling with not only the verbose syntax but also the expected style and approach requirements.
Given a simple FizzBuzz challenge I produced the following code:
public class FizzBuzz {
public static void main(String[] args) {
boolean hit;
for (int n = 1; n <= 30; n++) {
hit = false;
if (n % 3 == 0) {
System.out.print("Fizz");
hit = true;
}
if (n % 5 == 0) {
System.out.print("Buzz");
hit = true;
}
if (hit != true) {
System.out.print(n);
}
System.out.println();
}
}
}
Asked to refactor this code by the lead programmer and to consider possible future requirements and code managability issues I gave it some thought and produced the following refactored code:
public class FizzBuzz {
public static void main(String[] args) {
boolean hit;
for (int n = 1; n < 30; n++) {
hit = false;
hit = (n % 3 == 0) ? writeAction("Fizz") : hit;
hit = (n % 5 == 0) ? writeAction("Buzz") : hit;
if ( ! hit)
System.out.print(n);
System.out.println();
}
}
private static boolean writeAction(String actionWord){
System.out.print(actionWord);
return true;
}
}
However, the guy who set this task has moved on quite quickly and I never got any feedback on this approach. Am I going in the right direction with this or have I regressed?. To me this should scale better and would be easier to modify. I have also considered that maybe he was expecting some sort of TDD approach? I am aware that I have no tests currently.
This site isn't for reviews, but in case your question gets moved, here is some feedback (from the "clean code" perspective):
your "main" code sits in a main() method. Makes re-using it very hard.
talking about re-use - various things in there prevent re-using it
you have some duplicated code in there
you are violating the single layer of abstraction principle
How I would write something along the lines of:
public class FizzBuzz {
private final OutputStream out;
public FizzBuzz(OutputStream out) {
this.out = out;
}
public void runFizzBuzzUpTo(int n) {
for (int i = 1; i < n; i++) {
if ( writeIfTrue(n % 3 == 0, "Fizz") ) {
continue;
}
if ( writeIfTrue(n % 5 == 0, "Buzz") ) {
continue;
}
out.println(n);
}
}
private boolean writeIfTrue(boolean toCheck, String word) {
if (toCheck) {
out.println(word);
}
return toCheck;
}
public static void main(String[] args) {
new FizzBuzz(System.out).runFizzBuzzUpto(30);
}
}
Things I changed:
made the output the "core" thing of a class
provided the possibility to run for arbitrary positive numbers
Stuff still missing:
"single layere of abstraction" is still not good
instead of fixing "30" in main() - one could check for exactly one argument passed to main() - which would then be used as parameter for runFizzBuzzUpTo()
Of course, the second code is more modular and easier to modify that way. I mostly don't prefer to write the if conditions in the short way...
The method writeAction could be void because you don't have to return anything.
But you have good ideas :)

Print Tree with 4 nodes (simple forest) for checking a benchmark

I implemented an experimental OOP language and now benchmark garbage collection using a Storage benchmark. Now I want to check/print the following benchmark for small depths (n=2, 3, 4,..).
The tree (forest with 4 subnode) is generated by the buildTreeDepth method. The code is as follows:
import java.util.Arrays;
public final class StorageSimple {
private int count;
private int seed = 74755;
public int randomNext() {
seed = ((seed * 1309) + 13849) & 65535;
return seed;
}
private Object buildTreeDepth(final int depth) {
count++;
if (depth == 1) {
return new Object[randomNext() % 10 + 1];
} else {
Object[] arr = new Object[4];
Arrays.setAll(arr, v -> buildTreeDepth(depth - 1));
return arr;
}
}
public Object benchmark() {
count = 0;
buildTreeDepth(7);
return count;
}
public boolean verifyResult(final Object result) {
return 5461 == (int) result;
}
public static void main(String[] args) {
StorageSimple store = new StorageSimple();
System.out.println("Result: " + store.verifyResult(store.benchmark()));
}
}
Is there a somewhat simple/straight forward way to print the tree generated by buildTreeDepth? Just the short trees of n=3, 4, 5.
As other has already suggested, you may choose some lib to do so. But if you just want a simple algo to test in command line, you may do the following, which I always use when printing tree in command line (write by handle, may have some bug. Believe you can get what this BFS algo works):
queue.add(root);
queue.add(empty);
int count = 1;
while (queue.size() != 1) {
Node poll = queue.poll();
if (poll == empty) {
count = 1;
queue.add(empty);
}
for (Node n : poll.getChildNodes()) {
n.setNodeName(poll.getNodeName(), count++);
queue.add(n);
}
System.out.println(poll.getNodeName());
}
Sample output:
1
1-1 1-2 1-3 1-4
1-1-1 1-1-2 1-1-3 1-2-1 1-2-2 1-3-1 1-3-2 1-4-1
...
And in your case you use array, which seems even easier to print.
Instead of using object arrays, use a List implementation like ArrayList. For an improved better result subclass ArrayList to also hold a 'level' value and add indentation to the toString() method.

Performance of java.lang.reflect.Array

Since I'm making heavy use of reflective access to arrays in a project, I decided to compare the performance of array[index] vs java.lang.reflect.Array.get(array, index). While I anticipated, that reflective calls are quite a bit slower, I was surprised to see that they are between 10-16 times slower.
So I decided to write a simple utility method that does about the same as Array#get but receives the array at the given index by casting the object instead of using a native method (as does Array#get):
public static Object get(Object array, int index){
Class<?> c = array.getClass();
if (int[].class == c) {
return ((int[])array)[index];
} else if (float[].class == c) {
return ((float[])array)[index];
} else if (boolean[].class == c) {
return ((boolean[])array)[index];
} else if (char[].class == c) {
return ((char[])array)[index];
} else if (double[].class == c) {
return ((double[])array)[index];
} else if (long[].class == c) {
return ((long[])array)[index];
} else if (short[].class == c) {
return ((short[])array)[index];
} else if (byte[].class == c) {
return ((byte[])array)[index];
}
return ((Object[])array)[index];
}
I believe that this method provides the same functionality as Array#get, with the notable difference of the thrown exceptions (e.g. a ClassCastException gets thrown instead of an IllegalArgumentException, if one calls the method with an Object that is no array.).
To my surprise, this utility method performs much better than Array#get.
Three questions:
Do others here experience the same performance issues with Array#get, or is this perhaps a hardware/platform/Java-version issue (I tested with Java 8 on a dual core Windows 7 laptop)?
Do I miss something concerning the functionality of the method Array#get? I.e. is there some functionality that must necessarily be implemented using a native call?
Is there a specific reason, why Array#get was implemented using native methods, when the same functionality could have been implemented in pure Java with a much higher performance?
Test Classes and Results
The tests have been done using Caliper (latest Caliper from git necessary to compile the code). But for your convenience I also included a main method that performs a simplified test (you need to remove the Caliper annotations to make it compile).
TestClass:
import java.lang.reflect.Array;
import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
public class ArrayAtBenchmark {
public static final class ArrayUtil {
public static Object get(Object array, int index){
Class<?> c = array.getClass();
if (int[].class == c) {
return ((int[])array)[index];
} else if (float[].class == c) {
return ((float[])array)[index];
} else if (boolean[].class == c) {
return ((boolean[])array)[index];
} else if (char[].class == c) {
return ((char[])array)[index];
} else if (double[].class == c) {
return ((double[])array)[index];
} else if (long[].class == c) {
return ((long[])array)[index];
} else if (short[].class == c) {
return ((short[])array)[index];
} else if (byte[].class == c) {
return ((byte[])array)[index];
}
return ((Object[])array)[index];
}
}
private static final int ELEMENT_SIZE = 100;
private Object[] objectArray;
#BeforeExperiment
public void setup(){
objectArray = new Object[ELEMENT_SIZE];
for (int i = 0; i < objectArray.length; i++) {
objectArray[i] = new Object();
}
}
#Benchmark
public int ObjectArray_at(int reps){
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int j = 0; j < ELEMENT_SIZE; j++) {
dummy |= objectArray[j].hashCode();
}
}
return dummy;
}
#Benchmark
public int ObjectArray_Array_get(int reps){
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int j = 0; j < ELEMENT_SIZE; j++) {
dummy |= Array.get(objectArray, j).hashCode();
}
}
return dummy;
}
#Benchmark
public int ObjectArray_ArrayUtil_get(int reps){
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int j = 0; j < ELEMENT_SIZE; j++) {
dummy |= ArrayUtil.get(objectArray, j).hashCode();
}
}
return dummy;
}
// test method to use without Cailper
public static void main(String[] args) {
ArrayAtBenchmark benchmark = new ArrayAtBenchmark();
benchmark.setup();
int warmup = 100000;
// warm up
benchmark.ObjectArray_at(warmup);
benchmark.ObjectArray_Array_get(warmup);
benchmark.ObjectArray_ArrayUtil_get(warmup);
int reps = 100000;
long start = System.nanoTime();
int temp = benchmark.ObjectArray_at(reps);
long end = System.nanoTime();
long time = (end-start)/reps;
System.out.println("time for ObjectArray_at: " + time + " NS");
start = System.nanoTime();
temp |= benchmark.ObjectArray_Array_get(reps);
end = System.nanoTime();
time = (end-start)/reps;
System.out.println("time for ObjectArray_Array_get: " + time + " NS");
start = System.nanoTime();
temp |= benchmark.ObjectArray_ArrayUtil_get(reps);
end = System.nanoTime();
time = (end-start)/reps;
System.out.println("time for ObjectArray_ArrayUtil_get: " + time + " NS");
if (temp == 0) {
// sanity check to prevent JIT to optimize the test methods away
System.out.println("result:" + result);
}
}
}
The Caliper results can be viewed here.
The results of the simplified main method look like this on my machine:
time for ObjectArray_at: 620 NS
time for ObjectArray_Array_get: 10525 NS
time for ObjectArray_ArrayUtil_get: 1287 NS
Additional information
The results are similar when running the JVM with "-server"
The other Array methods (e.g. Array#getInt, Array#getLength, Array#set etc.) also perform much slower than similarly implemented utility methods
This question is somewhat related to: What is the purpose of java.lang.reflect.Array's getter and setter methods?
Yes, Array.get is slow in OpenJDK / Oracle JDK because it is implemented by a native method and is not optimized by JIT.
There is no special reason for Array.get to be native except that it has been so from the earliest releases of JDK (when JVM was not so good and there was no JIT at all). Moreover, there is a pure Java compatible implementation of java.lang.reflect.Array from GNU Classpath.
Currently (as of JDK 8u45) only Array.newInstance and Array.getLength are optimized (being JVM intrinsics). Looks like nobody really cared about performance of reflective get/set methods. But as #Marco13 noticed there is an open issue JDK-8051447 to improve the performance of Array.* methods somewhen in future.

Return statement best practices in java?

I want to know the difference between these two codes even though they produce the same output:
CODE 1:
class ret {
public static int add(int x) {
if(x!=0)
return x+add(x-1);
return x;
}
public static void main(String args[]) {
System.out.println(add(5));
}
}
CODE 2:
class ret {
public static int add(int x) {
if(x!=0)
return x+add(x-1);
return 0;
}
public static void main(String args[]) {
System.out.println(add(5));
}
}
They both output 15 but how come the second code also output's 15 instead of zero?My understanding is that the last call would be add(0) for code 2 and it would return zero.I also want to know is it okay to use multiple return statements or use a single return statement and replace the rest with local variables.I remember reading that single entry single exit model is a good practice.
This is a recursive method, so when x != 0, you will return the result of "x added to calling the method again with (x-1)". The final call will always return x == 0 or constant = 0, so you will return 15 from both versions.
Single return vs. multiple return is a matter of debate. The former should be preferred as a rule. Generally it will be obvious where multiple return statements are acceptable as it will be simpler to understand the method with them than with the alternative code constructs required to engineer a single exit point. Also note you could rewrite add as:
public static int add(int x) {
return x == 0 ? 0 : (x + add(x-1));
}
Version 1:
add(5)
call add(4)
call add(3)
call add(2)
call add(1)
call add(0)
return (x = 0)
return (x = 1) + (add(x-1) = 0) = 1
return (x = 2) + (add(x-1) = 1) = 3
return (x = 3) + (add(x-1) = 3) = 6
return (x = 4) + (add(x-1) = 6) = 10
return (x = 5) + (add(x-1) = 10) = 15
Version 2:
add(5)
call add(4)
call add(3)
call add(2)
call add(1)
call add(0)
return (constant = 0) // the only difference
return (x = 1) + (add(x-1) = 0) = 1
return (x = 2) + (add(x-1) = 1) = 3
return (x = 3) + (add(x-1) = 3) = 6
return (x = 4) + (add(x-1) = 6) = 10
return (x = 5) + (add(x-1) = 10) = 15
The use of multiple return statement versus using a single exit point cannot be answered with an easy one-line answer. I guess the best answer you can get is "it depends on your company's standards".
Single exit point is a very good standard, even though I don't personally endorse it. You end up having methods that always have a single return statement at the end, so you never get in a position where you are looking for those many possible return statement while editing someone else's code. I believe that developers that used to code in C tend to follow this standard (see this question).
I, for one, perfer using multiple return statements when it can help simplify the code. One case where I like to use it is to prevent cascading braces in my code. For instance, in the following example:
private int doSomething (int param) {
int returnCode;
if (param >= 0) {
int someValue = param * CONSTANT_VALUE;
if (isWithinExpectedRange (someValue)) {
if (updateSomething (someValue)) {
returnCode = 0;
} else {
returnCode = -3;
}
} else {
returnCode = -2;
}
} else {
returnCode = -1;
}
return returnCode;
}
I find this type of coding to be very confusing when reading it. I tend to change this type of code to:
private int doSomething (int param) {
if (param < 0) {
return -1;
}
int someValue = param * CONSTANT_VALUE;
if (!isWithinExpectedRange (someValue)) {
return -2;
}
if (!updateSomething (someValue)) {
return -3;
}
return 0;
}
The second example looks cleaner, and clearer, to me. Even more when the actual code has some extra coding in the else blocks.
Again, this is personal tastes. Some company might enforce a single exit point, some might not, and some developers prefer single exit point. The bottom line is, if there's a guideline available for you to follow in your environment, then do so. If not, then you can chose your own preference base partly on these arguments.

what is wrong with this thread-safe byte sequence generator?

I need a byte generator that would generate values from Byte.MIN_VALUE to Byte.MAX_VALUE. When it reaches MAX_VALUE, it should start over again from MIN_VALUE.
I have written the code using AtomicInteger (see below); however, the code does not seem to behave properly if accessed concurrently and if made artificially slow with Thread.sleep() (if no sleeping, it runs fine; however, I suspect it is just too fast for concurrency problems to show up).
The code (with some added debug code):
public class ByteGenerator {
private static final int INITIAL_VALUE = Byte.MIN_VALUE-1;
private AtomicInteger counter = new AtomicInteger(INITIAL_VALUE);
private AtomicInteger resetCounter = new AtomicInteger(0);
private boolean isSlow = false;
private long startTime;
public byte nextValue() {
int next = counter.incrementAndGet();
//if (isSlow) slowDown(5);
if (next > Byte.MAX_VALUE) {
synchronized(counter) {
int i = counter.get();
//if value is still larger than max byte value, we reset it
if (i > Byte.MAX_VALUE) {
counter.set(INITIAL_VALUE);
resetCounter.incrementAndGet();
if (isSlow) slowDownAndLog(10, "resetting");
} else {
if (isSlow) slowDownAndLog(1, "missed");
}
next = counter.incrementAndGet();
}
}
return (byte) next;
}
private void slowDown(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
}
}
private void slowDownAndLog(long millis, String msg) {
slowDown(millis);
System.out.println(resetCounter + " "
+ (System.currentTimeMillis()-startTime) + " "
+ Thread.currentThread().getName() + ": " + msg);
}
public void setSlow(boolean isSlow) {
this.isSlow = isSlow;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
}
And, the test:
public class ByteGeneratorTest {
#Test
public void testGenerate() throws Exception {
ByteGenerator g = new ByteGenerator();
for (int n = 0; n < 10; n++) {
for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
assertEquals(i, g.nextValue());
}
}
}
#Test
public void testGenerateMultiThreaded() throws Exception {
final ByteGenerator g = new ByteGenerator();
g.setSlow(true);
final AtomicInteger[] counters = new AtomicInteger[Byte.MAX_VALUE-Byte.MIN_VALUE+1];
for (int i = 0; i < counters.length; i++) {
counters[i] = new AtomicInteger(0);
}
Thread[] threads = new Thread[100];
final CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
try {
for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
byte value = g.nextValue();
counters[value-Byte.MIN_VALUE].incrementAndGet();
}
} finally {
latch.countDown();
}
}
}, "generator-client-" + i);
threads[i].setDaemon(true);
}
g.setStartTime(System.currentTimeMillis());
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
latch.await();
for (int i = 0; i < counters.length; i++) {
System.out.println("value #" + (i+Byte.MIN_VALUE) + ": " + counters[i].get());
}
//print out the number of hits for each value
for (int i = 0; i < counters.length; i++) {
assertEquals("value #" + (i+Byte.MIN_VALUE), threads.length, counters[i].get());
}
}
}
The result on my 2-core machine is that value #-128 gets 146 hits (all of them should get 100 hits equally as we have 100 threads).
If anyone has any ideas, what's wrong with this code, I'm all ears/eyes.
UPDATE: for those who are in a hurry and do not want to scroll down, the correct (and shortest and most elegant) way to solve this in Java would be like this:
public byte nextValue() {
return (byte) counter.incrementAndGet();
}
Thanks, Heinz!
Initially, Java stored all fields as 4 or 8 byte values, even short and byte. Operations on the fields would simply do bit masking to shrink the bytes. Thus we could very easily do this:
public byte nextValue() {
return (byte) counter.incrementAndGet();
}
Fun little puzzle, thanks Neeme :-)
You make the decision to incrementAndGet() based on a old value of counter.get(). The value of the counter can reach MAX_VALUE again before you do the incrementAndGet() operation on the counter.
if (next > Byte.MAX_VALUE) {
synchronized(counter) {
int i = counter.get(); //here You make sure the the counter is not over the MAX_VALUE
if (i > Byte.MAX_VALUE) {
counter.set(INITIAL_VALUE);
resetCounter.incrementAndGet();
if (isSlow) slowDownAndLog(10, "resetting");
} else {
if (isSlow) slowDownAndLog(1, "missed"); //the counter can reach MAX_VALUE again if you wait here long enough
}
next = counter.incrementAndGet(); //here you increment on return the counter that can reach >MAX_VALUE in the meantime
}
}
To make it work one has to make sure the no decisions are made on stale info. Either reset the counter or return the old value.
public byte nextValue() {
int next = counter.incrementAndGet();
if (next > Byte.MAX_VALUE) {
synchronized(counter) {
next = counter.incrementAndGet();
//if value is still larger than max byte value, we reset it
if (next > Byte.MAX_VALUE) {
counter.set(INITIAL_VALUE + 1);
next = INITIAL_VALUE + 1;
resetCounter.incrementAndGet();
if (isSlow) slowDownAndLog(10, "resetting");
} else {
if (isSlow) slowDownAndLog(1, "missed");
}
}
}
return (byte) next;
}
Your synchronized block contains only the if body. It should wrap whole method including if statement itself. Or just make your method nextValue synchronized. BTW in this case you do not need Atomic variables at all.
I hope this will work for you. Try to use Atomic variables only if your really need highest performance code, i.e. synchronized statement bothers you. IMHO in most cases it does not.
If I understand you correctly, you care that the results of nextValue are in the range of Byte.MIN_VALUE and Byte.MAX_VALUE and you don't care about the value stored in the counter.
Then you can map integers on bytes such that you required enumeration behavior is exposed:
private static final int VALUE_RANGE = Byte.MAX_VALUE - Byte.MIN_VALUE + 1;
private final AtomicInteger counter = new AtomicInteger(0);
public byte nextValue() {
return (byte) (counter.incrementAndGet() % VALUE_RANGE + Byte.MIN_VALUE - 1);
}
Beware, this is untested code. But the idea should work.
I coded up the following version of nextValue using compareAndSet which is designed to be used in a non-synchronized block. It passed your unit tests:
Oh, and I introduced new constants for MIN_VALUE and MAX_VALUE but you can ignore those if you prefer.
static final int LOWEST_VALUE = Byte.MIN_VALUE;
static final int HIGHEST_VALUE = Byte.MAX_VALUE;
private AtomicInteger counter = new AtomicInteger(LOWEST_VALUE - 1);
private AtomicInteger resetCounter = new AtomicInteger(0);
public byte nextValue() {
int oldValue;
int newValue;
do {
oldValue = counter.get();
if (oldValue >= HIGHEST_VALUE) {
newValue = LOWEST_VALUE;
resetCounter.incrementAndGet();
if (isSlow) slowDownAndLog(10, "resetting");
} else {
newValue = oldValue + 1;
if (isSlow) slowDownAndLog(1, "missed");
}
} while (!counter.compareAndSet(oldValue, newValue));
return (byte) newValue;
}
compareAndSet() works in conjunction with get() to manage concurrency.
At the start of your critical section, you perform a get() to retrieve the old value. You then perform some function dependent only on the old value to compute a new value. Then you use compareAndSet() to set the new value. If the AtomicInteger is no longer equal to the old value at the time compareAndSet() is executed (because of concurrent activity), it fails and you must start over.
If you have an extreme amount of concurrency and the computation time is long, it is conceivable that the compareAndSet() may fail many times before succeeding and it may be worth gathering statistics on that if concerns you.
I'm not suggesting that this is a better or worse approach than a simple synchronized block as others have suggested, but I personally would probably use a synchronized block for simplicity.
EDIT: I'll answer your actual question "Why doesn't mine work?"
Your code has:
int next = counter.incrementAndGet();
if (next > Byte.MAX_VALUE) {
As these two lines are not protected by a synchronized block, multiple threads can execute them concurrently and all obtain values of next > Byte.MAX_VALUE. All of them will then drop through into the synchronized block and set counter back to INITIAL_VALUE (one after another as they wait for each other).
Over the years, there has been a huge amount written over the pitfalls of trying to get a performance tweak by not synchronizing when it doesn't seem necessary. For example, see Double Checked Locking
Notwithstanding that Heinz Kabutz is the clean answer to the specific question, ye olde Java SE 8 [March 2014] added AtomicIntger.updateAndGet (and friends). This leads to a more general solution if circumstances required:
public class ByteGenerator {
private static final int MIN = Byte.MIN_VALUE;
private static final int MAX = Byte.MAX_VALUE;
private final AtomicInteger counter = new AtomicInteger(MIN);
public byte nextValue() {
return (byte)counter.getAndUpdate(ByteGenerator::update);
}
private static int update(int old) {
return old==MAX ? MIN : old+1;
}
}

Categories

Resources