In daily java development, before jdk8, the operation of sets was troublesome, especially for the judgment of empty sets, it is necessary to write some repeated and similar codes to judge. However, after jdk8, there are rich set interfaces under the concurrent package, which simplifies the complexity of using sets before Here are some useful features that are easy to ignore
Delay queue
In development, if you need to add elements to a queue set, but want it to delay execution, the {DelayQueue queue queue is recommended here In this way, you can set the expiration time of this element and object when you join the queue. When the expiration time is up, it will come out of the queue It is necessary to implement the Delayed interface. Rewrite the getDelay and compareTo function codes as follows:
@Data public static class DelayedEvent implements Delayed { private long startTime; private String message; public DelayedEvent(long startTime, String message) { this.startTime = startTime; this.message = message; } @Override public long getDelay(TimeUnit unit) { long diff = this.startTime - System.currentTimeMillis(); return unit.convert(diff, TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed o) { return (int) (this.startTime - ((DelayedEvent) o).startTime); } }
The calling code is as follows. After adding elements, the columns will be listed after 10S:
final DelayQueue<DelayedEvent> delayQueue = new DelayQueue<>(); final long timeFirst = System.currentTimeMillis() + 10000; delayQueue.offer(new DelayedEvent(timeFirst, "hello word")); log.info("add done"); log.info(delayQueue.take().getMessage());
The results are as follows:
09:42:02.732 [main] INFO com.test.util.queue - add done
09:42:12.729 [main] INFO com.test.util.queue - hello word
Time format date
Before jdk16, if you want to judge whether it is morning or afternoon according to the current time, you need to implement the function yourself. In the function, you can judge according to the different time periods of the day. After jdk14, you can use the following function:
In this way, with the change of time, the returned time prompt is also changing dynamically
String timeHit = DateTimeFormatter.ofPattern("B").format(LocalDateTime.now());
System.out.println(timeHit);
The DateTimeFormatter supports the following alphabetic list:
The results are returned as follows:
Concurrent accumulator
It is also called a concurrent adder, which allows us to implement a lock free algorithm in many scenarios. The java concurrent package provides a long accumulator (or double accumulator) to update the value using the provided function,
In a multithreaded scenario, if we want to update a value of type int and long, we usually use atomicinteger and atomiclong Given that the initial value is 10, then 50 threads call concurrently, adding 1 each time. Here, we directly use the} Accumulator structure:
LongAccumulator accumulator = new LongAccumulator(Long::sum, 10); Runnable w = () -> accumulator.accumulate(1); ExecutorService executor = Executors.newFixedThreadPool(50); for (int i = 0; i < 50; i++) { executor.submit(w); } executor.shutdown();
System.out.println("Balance: " + accumulator.get());
The results are returned as follows:
Hexadecimal format
In the JDK 8 version, if you need to convert hexadecimal, string and byte types to each other, the conversion operation is slightly more complex In JDK version 17, the HexFormat class is provided to complete the mutual conversion between them The code is as follows:
HexFormat format = HexFormat.of();
byte[] input = new byte[] {100, 20, -10, 30}; String hex = format.formatHex(input); System.out.println(hex); byte[] output = format.parseHex(hex); assert Arrays.compare(input, output) == 0;
Binary search of array
In jdk1 Before 8, in a given ordered array set, the position of an element was found, if any, the element position was returned, but - 1 was not returned. This requires manual implementation of the binary search algorithm, which approximately bisects the element each time, and then searches In jdk1 After 8, the underlying algorithm support is provided,
For example, in a given ordered set, find the element X. if x exists, return the position of the element. If the element does not exist, return the position index of the inserted element. The code is as follows:
int[] t = new int[]{10, 15, 20, 21}; int idx = Arrays.binarySearch(t, 15); System.out.println("Element location:" + idx); idx = Arrays.binarySearch(t, 100); System.out.println("Where to insert the element:" +~idx);
Here is a supplement:
Conclusion summary of inverse operator (~):
When n is a positive number, ~ (n) = -(n+1)
When n is negative, ~ (- n) = n - 1, ignoring the negative sign
The results are as follows:
Bit Set
If we need to operate on a bit array, the general operation is that we declare an array of boolean type, and each bit represents true and fasle(0 and 1) with Boolean With the bit set class, we can declare it BItSet This class allows us to store and manipulate an array of bits. Compared with the Boolean value [], it consumes 8 times less memory. We can perform logical operations on the array, for example, and, or, xor (which can be understood as union, intersection and difference in the set). The code is as follows:
BitSet bs1 = new BitSet(); bs1.set(0); bs1.set(2); bs1.set(4); System.out.println("bs1 : " + bs1); BitSet bs2 = new BitSet(); bs2.set(1); bs2.set(2); bs2.set(3); System.out.println("bs2 : " + bs2); bs2.xor(bs1); System.out.println("xor: " + bs2); bs2.and(bs1); System.out.println("and: " + bs2);
The results are as follows:
Phaser
Phaser solves the problem of phased concurrency, which is very similar to countdown. However, it provides some additional functionality. It allows us to set the dynamic number of threads to wait before proceeding. When using phaser, the defined number of threads needs to wait on the barrier before entering the next step of execution. Because of this, we can coordinate multiple implementation stages Three threads are defined. These three threads have three phases. After the first phase is executed, the next phase is executed. The code is as follows:
Phaser phaser = new Phaser(3); Runnable r = () -> { System.out.println("phase-0"); phaser.arriveAndAwaitAdvance(); System.out.println("phase-1"); phaser.arriveAndAwaitAdvance(); System.out.println("phase-2"); phaser.arriveAndDeregister(); }; ExecutorService executor = Executors.newFixedThreadPool(3); for (int i = 0; i < 3; i++) { executor.submit(r); }
The results are as follows:
Stamped Lock
java Concurrent is one of the most interesting java packages, which provides many tools and collections for concurrent use under multithreading The core idea of the lock is that if a write occurs during reading, the new value should be obtained by retry instead of blocking the write operation. This mode is a typical lockless programming idea, which is the same as CAS spin. This operation mode determines that StampedLock is very applicable in the scenario of many read threads and very few write threads, and also avoids the occurrence of write starvation Is an alternative to ReadWriteLock, which allows optimistic locking of read operations. Moreover, its performance is better than ReentrantReadWriteLock. The following example is a concurrent execution of multiple threads. One thread is a self increasing operation on a number, and the other thread is the result of reading the latest write. In fact, according to the characteristics of the, the read adopts an optimistic lock, which will not block the write operation in theory
public class Number {
private int acc;
public Number(int amount) {
this.acc = amount;
}
}
StampedLock lock = new StampedLock(); Number b = new Number(10); Runnable w = () -> { long stamp = lock.writeLock(); b.setAcc(b.getAcc() + 1); System.out.println("acc: " + b.getAcc()); lock.unlockWrite(stamp); }; Runnable r = () -> { long stamp = lock.tryOptimisticRead(); if (!lock.validate(stamp)) { stamp = lock.readLock(); try { System.out.println("read acc: " + b.getAcc()); } finally { lock.unlockRead(stamp); } } else { System.out.println("Optimistic read fails"); } }; ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { executor.submit(w); executor.submit(r); }
As a direct result, the process executed on no machine may be different, depending on the speed of the cpu, but the final result must be 20
The results are as follows: