# 自定义工具类
# Socket 工具类
SocketUtil.java
解决粘包/半包问题:
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
public class SocketUtil {
private static final int BUF_SIZE = 1024;
/**
* 从 buf 中查找 "\r\n",并返回这段(包括 \r\n)空间的长度。
* 如果 buf 中没有,则返回 0 。
*/
public static int findLineEnd(final byte[] buf, int rlen) {
int splitbyte = 0;
while (splitbyte + 1 < rlen) {
if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && splitbyte + 1 < rlen) {
return splitbyte + 2;
}
splitbyte++;
}
return 0;
}
/**
* 半包、粘包问题解决:
* 从 InputStream 中找一行数据并返回。“一行”的标志是截止到 \r\n,不包括 \r\n 。
*/
public static String readLine(InputStream inputStream) throws IOException, RuntimeException {
int splitbyte = 0;
int rlen = 0;
byte[] buf = new byte[BUF_SIZE];
int read = -1;
inputStream.mark(BUF_SIZE);
try {
read = inputStream.read(buf, 0, BUF_SIZE);
} catch (Exception e) {
throw new SocketException("Could not Read");
}
if (read == -1) {
throw new SocketException("Peer is Closed.");
}
/* buf 中存储的数据区间为: buf[0] ... buf[read-1],共 read 个字节。 */
while (read > 0) {
rlen += read;
splitbyte = findLineEnd(buf, rlen);
if (splitbyte > 0) break;
if (inputStream.available() == 0) break;
read = inputStream.read(buf, rlen, BUF_SIZE - rlen);
}
/* buf 中存储的数据区间为: buf[0] ... buf[splitbyte-1],共 splitbyte 个字节。其中包含 \r\n 在内。*/
// -2 的目的是返回的结果内容中不包含 \r\n 。
return new String(buf, 0, splitbyte - 2);
}
public static void safeClose(Closeable closeable) {
if (closeable == null)
return;
try {
if (closeable instanceof ServerSocket) {
if (!((ServerSocket) closeable).isClosed())
((ServerSocket) closeable).close();
} else if (closeable instanceof Socket) {
if (!((Socket) closeable).isClosed())
((Socket) closeable).close();
} else if (closeable instanceof Closeable) {
closeable.close();
} else {
throw new RuntimeException();
}
} catch (IOException e) {
System.out.println("Could not close: " + e.getMessage());
}
}
}
# Json 工具类
JsonUtil.java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JsonUtil {
public static <T> String toJSONString(Object object) {
try {
return new ObjectMapper().writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
}
public static <T> T parseObject(String JSONString, Class<T> clazz) {
if (!JSONString.startsWith("{") && !JSONString.endsWith("}"))
throw new IllegalArgumentException();
try {
return new ObjectMapper().readValue(JSONString, clazz);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
}
public static <T> List<T> parseObjectList(String JSONString, Class<T> elementClass) {
if (!JSONString.startsWith("[") && !JSONString.endsWith("]"))
throw new IllegalArgumentException();
JavaType javaType = getCollectionType(ArrayList.class, elementClass);
try {
return (List<T>) new ObjectMapper().readValue(JSONString, javaType);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
private static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
return new ObjectMapper()
.getTypeFactory()
.constructParametricType(collectionClass, elementClasses);
}
}
# ThreadLocal 工具类样板
以在 ThreadLocal 中存放 Connection 为例。
ConnectionContainer.java
public class ConnectionContainer {
private static ThreadLocal<Connection> container = new ThreadLocal<>();
public static Connection get() {
Connection connection = container.get();
if (connection == null) {
// 从数据库连接池中获得 Connection 对象
...
container.set(connection);
}
return connection;
}
public static void clear() {
Connection connection = container.get();
if (connection == null)
return;
container.remove();
}
}
# JWT 工具类
JwtUtil.java
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import java.util.Collection;
import java.util.Map;
@Slf4j
public class JwtUtil {
private static final String usernameKey = "username";
private static final String authoritiesKey = "authorities";
public static final String secret = "hello world goodbye thank you very much see you next time";
static {
log.info("spring security jwt secret: {}", secret);
}
@SneakyThrows
public static String createJWT(String username) {
// jwt 荷载
JSONObject obj = new JSONObject();
obj.put(usernameKey, username);
return createJWT(obj);
}
@SneakyThrows
public static boolean verify(String jwtString) {
JWSObject jwsObject = JWSObject.parse(jwtString);
JWSVerifier jwsVerifier = new MACVerifier(secret);
return jwsObject.verify(jwsVerifier);
}
@SneakyThrows
public static String getUsernameFromJWT(String jwtString) {
JWSObject jwsObject = JWSObject.parse(jwtString);
Map<String, Object> map = jwsObject.getPayload().toJSONObject();
return (String) map.get(usernameKey);
}
@SneakyThrows
public static String getAuthoritiesFromJwt(String jwtString) {
JWSObject jwsObject = JWSObject.parse(jwtString);
Map<String, Object> map = jwsObject.getPayload().toJSONObject();
return (String) map.get(authoritiesKey);
}
private static String createJWT(JSONObject payload) throws JOSEException {
// jwt 头
JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256).type(JOSEObjectType.JWT).build();
// jwt 头 + 荷载 + 密钥 = 签名
JWSSigner jwsSigner = new MACSigner(secret);
JWSObject jwsObject = new JWSObject(jwsHeader, new Payload(payload));
// 进行签名(根据前两部分生成第三部分)
jwsObject.sign(jwsSigner);
// 获得 jwt string
return jwsObject.serialize();
}
}
# Map 工具类
MapUtil.java
public class MapUtil {
/**
* Creates an instance of {@code HashMap}
*/
public static <K, V> HashMap<K, V> newHashMap() {
return new HashMap<>();
}
/**
* Returns the empty map.
*/
public static <K, V> Map<K, V> of() {
return newHashMap();
}
/**
* Returns map containing a single entry.
*/
public static <K, V> Map<K, V> of(K k1, V v1) {
Map<K, V> map = of();
map.put(k1, v1);
return map;
}
/**
* Returns map containing the given entries.
*/
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {
Map<K, V> map = of(k1, v1);
map.put(k2, v2);
return map;
}
/**
* Returns map containing the given entries.
*/
public static <K, V> Map<K, V> of(K k1, V v1,
K k2, V v2,
K k3, V v3) {
Map<K, V> map = of(k1, v1, k2, v2);
map.put(k3, v3);
return map;
}
/**
* Returns map containing the given entries.
*/
public static <K, V> Map<K, V> of(K k1, V v1,
K k2, V v2,
K k3, V v3,
K k4, V v4) {
Map<K, V> map = of(k1, v1, k2, v2, k3, v3);
map.put(k4, v4);
return map;
}
/**
* Returns map containing the given entries.
*/
public static <K, V> Map<K, V> of(K k1, V v1,
K k2, V v2,
K k3, V v3,
K k4, V v4,
K k5, V v5) {
Map<K, V> map = of(k1, v1, k2, v2, k3, v3, k4, v4);
map.put(k5, v5);
return map;
}
/**
* Returns map containing the given entries.
*/
public static <K, V> Map<K, V> of(K k1, V v1,
K k2, V v2,
K k3, V v3,
K k4, V v4,
K k5, V v5,
K k6, V v6) {
Map<K, V> map = of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
map.put(k6, v6);
return map;
}
/**
* Returns map containing the given entries.
*/
public static <K, V> Map<K, V> of(K k1, V v1,
K k2, V v2,
K k3, V v3,
K k4, V v4,
K k5, V v5,
K k6, V v6,
K k7, V v7) {
Map<K, V> map = of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6);
map.put(k7, v7);
return map;
}
public static <K, V> Map<K, V> of(K k1, V v1,
K k2, V v2,
K k3, V v3,
K k4, V v4,
K k5, V v5,
K k6, V v6,
K k7, V v7,
K k8, V v8) {
Map<K, V> map = of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7);
map.put(k8, v8);
return map;
}
/**
* Returns map containing the given entries.
*/
public static <K, V> Builder<K, V> builder() {
return new Builder<>();
}
public static final class Builder<K, V> {
private Map<K, V> map;
private boolean underConstruction;
private Builder() {
map = newHashMap();
underConstruction = true;
}
public Builder<K, V> put(K k, V v) {
if (!underConstruction) {
throw new IllegalStateException("Underlying map has already been built");
}
map.put(k, v);
return this;
}
public Map<K, V> build() {
if (!underConstruction) {
throw new IllegalStateException("Underlying map has already been built");
}
underConstruction = false;
return map;
}
}
}
# MT19937 随机算法实现
Mersenne Twister 算法译为马特赛特旋转演算法,是伪随机数发生器之一,其主要作用是生成伪随机数。此算法是 Makoto Matsumoto(松本)和 Takuji Nishimura(西村)于 1997 年开发的,基于有限二进制字段上的矩阵线性再生。可以快速产生高质量的伪随机数,修正了古老随机数产生算法的很多缺陷。
Mersenne Twister 这个名字来自周期长度通常取 Mersenne 质数这样一个事实。常见的有两个变种 Mersenne Twister MT19937 和 Mersenne Twister MT19937-64 。
Mersenne Twister 有以下优点:随机性好,在计算机上容易实现,占用内存较少(mt19937 的 C 程式码执行仅需 624 个字的工作区域),与其它已使用的伪随机数发生器相比,产生随机数的速度快、周期长,可达到 2^19937-1,且具有 623 维均匀分布的性质,对于一般的应用来说,足够大了,序列关联比较小,能通过很多随机性测试。
MTRandom.java
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* MT19937 的 Java 实现。用于高效生成伪随机数。
*/
public class MTRandom extends Random {
// Constants used in the original C implementation
private final static int UPPER_MASK = 0x80000000;
private final static int LOWER_MASK = 0x7fffffff;
private final static int N = 624;
private final static int M = 397;
private final static int MAGIC[] = {0x0, 0x9908b0df};
private final static int MAGIC_FACTOR1 = 1812433253;
private final static int MAGIC_FACTOR2 = 1664525;
private final static int MAGIC_FACTOR3 = 1566083941;
private final static int MAGIC_MASK1 = 0x9d2c5680;
private final static int MAGIC_MASK2 = 0xefc60000;
private final static int MAGIC_SEED = 19650218;
private final static long DEFAULT_SEED = 5489L;
// Internal state
private transient int[] mt;
private transient int mti;
private transient boolean compat = false;
// Temporary buffer used during setSeed(long)
private transient int[] ibuf;
/**
* The default constructor for an instance of MTRandom. This invokes
* the no-argument constructor for java.util.Random which will result
* in the class being initialised with a seed value obtained by calling
* System.currentTimeMillis().
*/
public MTRandom() {
}
/**
* This version of the constructor can be used to implement identical
* behaviour to the original C code version of this algorithm including
* exactly replicating the case where the seed value had not been set
* prior to calling genrand_int32.
* <p>
* If the compatibility flag is set to true, then the algorithm will be
* seeded with the same default value as was used in the original C
* code. Furthermore the setSeed() method, which must take a 64 bit
* long value, will be limited to using only the lower 32 bits of the
* seed to facilitate seamless migration of existing C code into Java
* where identical behaviour is required.
* <p>
* Whilst useful for ensuring backwards compatibility, it is advised
* that this feature not be used unless specifically required, due to
* the reduction in strength of the seed value.
*
* @param compatible Compatibility flag for replicating original
* behaviour.
*/
public MTRandom(boolean compatible) {
super(0L);
compat = compatible;
setSeed(compat ? DEFAULT_SEED : System.currentTimeMillis());
}
/**
* This version of the constructor simply initialises the class with
* the given 64 bit seed value. For a better random number sequence
* this seed value should contain as much entropy as possible.
*
* @param seed The seed value with which to initialise this class.
*/
public MTRandom(long seed) {
super(seed);
}
/**
* This version of the constructor initialises the class with the
* given byte array. All the data will be used to initialise this
* instance.
*
* @param buf The non-empty byte array of seed information.
* @throws NullPointerException if the buffer is null.
* @throws IllegalArgumentException if the buffer has zero length.
*/
public MTRandom(byte[] buf) {
super(0L);
setSeed(buf);
}
/**
* This version of the constructor initialises the class with the
* given integer array. All the data will be used to initialise
* this instance.
*
* @param buf The non-empty integer array of seed information.
* @throws NullPointerException if the buffer is null.
* @throws IllegalArgumentException if the buffer has zero length.
*/
public MTRandom(int[] buf) {
super(0L);
setSeed(buf);
}
// Initializes mt[N] with a simple integer seed. This method is
// required as part of the Mersenne Twister algorithm but need
// not be made public.
private final void setSeed(int seed) {
// Annoying runtime check for initialisation of internal data
// caused by java.util.Random invoking setSeed() during init.
// This is unavoidable because no fields in our instance will
// have been initialised at this point, not even if the code
// were placed at the declaration of the member variable.
if (mt == null) mt = new int[N];
// ---- Begin Mersenne Twister Algorithm ----
mt[0] = seed;
for (mti = 1; mti < N; mti++) {
mt[mti] = (MAGIC_FACTOR1 * (mt[mti - 1] ^ (mt[mti - 1] >>> 30)) + mti);
}
// ---- End Mersenne Twister Algorithm ----
}
/**
* This method resets the state of this instance using the 64
* bits of seed data provided. Note that if the same seed data
* is passed to two different instances of MTRandom (both of
* which share the same compatibility state) then the sequence
* of numbers generated by both instances will be identical.
* <p>
* If this instance was initialised in 'compatibility' mode then
* this method will only use the lower 32 bits of any seed value
* passed in and will match the behaviour of the original C code
* exactly with respect to state initialisation.
*
* @param seed The 64 bit value used to initialise the random
* number generator state.
*/
public final synchronized void setSeed(long seed) {
if (compat) {
setSeed((int) seed);
} else {
// Annoying runtime check for initialisation of internal data
// caused by java.util.Random invoking setSeed() during init.
// This is unavoidable because no fields in our instance will
// have been initialised at this point, not even if the code
// were placed at the declaration of the member variable.
if (ibuf == null) ibuf = new int[2];
ibuf[0] = (int) seed;
ibuf[1] = (int) (seed >>> 32);
setSeed(ibuf);
}
}
/**
* This method resets the state of this instance using the byte
* array of seed data provided. Note that calling this method
* is equivalent to calling "setSeed(pack(buf))" and in particular
* will result in a new integer array being generated during the
* call. If you wish to retain this seed data to allow the pseudo
* random sequence to be restarted then it would be more efficient
* to use the "pack()" method to convert it into an integer array
* first and then use that to re-seed the instance. The behaviour
* of the class will be the same in both cases but it will be more
* efficient.
*
* @param buf The non-empty byte array of seed information.
* @throws NullPointerException if the buffer is null.
* @throws IllegalArgumentException if the buffer has zero length.
*/
public final void setSeed(byte[] buf) {
setSeed(pack(buf));
}
/**
* This method resets the state of this instance using the integer
* array of seed data provided. This is the canonical way of
* resetting the pseudo random number sequence.
*
* @param buf The non-empty integer array of seed information.
* @throws NullPointerException if the buffer is null.
* @throws IllegalArgumentException if the buffer has zero length.
*/
public final synchronized void setSeed(int[] buf) {
int length = buf.length;
if (length == 0) throw new IllegalArgumentException("Seed buffer may not be empty");
// ---- Begin Mersenne Twister Algorithm ----
int i = 1, j = 0, k = (N > length ? N : length);
setSeed(MAGIC_SEED);
for (; k > 0; k--) {
mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >>> 30)) * MAGIC_FACTOR2)) + buf[j] + j;
i++;
j++;
if (i >= N) {
mt[0] = mt[N - 1];
i = 1;
}
if (j >= length) j = 0;
}
for (k = N - 1; k > 0; k--) {
mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >>> 30)) * MAGIC_FACTOR3)) - i;
i++;
if (i >= N) {
mt[0] = mt[N - 1];
i = 1;
}
}
mt[0] = UPPER_MASK; // MSB is 1; assuring non-zero initial array
// ---- End Mersenne Twister Algorithm ----
}
/**
* This method forms the basis for generating a pseudo random number
* sequence from this class. If given a value of 32, this method
* behaves identically to the genrand_int32 function in the original
* C code and ensures that using the standard nextInt() function
* (inherited from Random) we are able to replicate behaviour exactly.
* <p>
* Note that where the number of bits requested is not equal to 32
* then bits will simply be masked out from the top of the returned
* integer value. That is to say that:
* <pre>
* mt.setSeed(12345);
* int foo = mt.nextInt(16) + (mt.nextInt(16) << 16);</pre>
* will not give the same result as
* <pre>
* mt.setSeed(12345);
* int foo = mt.nextInt(32);</pre>
*
* @param bits The number of significant bits desired in the output.
* @return The next value in the pseudo random sequence with the
* specified number of bits in the lower part of the integer.
*/
protected final synchronized int next(int bits) {
// ---- Begin Mersenne Twister Algorithm ----
int y, kk;
if (mti >= N) { // generate N words at one time
// In the original C implementation, mti is checked here
// to determine if initialisation has occurred; if not
// it initialises this instance with DEFAULT_SEED (5489).
// This is no longer necessary as initialisation of the
// Java instance must result in initialisation occurring
// Use the constructor MTRandom(true) to enable backwards
// compatible behaviour.
for (kk = 0; kk < N - M; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + M] ^ (y >>> 1) ^ MAGIC[y & 0x1];
}
for (; kk < N - 1; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + (M - N)] ^ (y >>> 1) ^ MAGIC[y & 0x1];
}
y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N - 1] = mt[M - 1] ^ (y >>> 1) ^ MAGIC[y & 0x1];
mti = 0;
}
y = mt[mti++];
// Tempering
y ^= (y >>> 11);
y ^= (y << 7) & MAGIC_MASK1;
y ^= (y << 15) & MAGIC_MASK2;
y ^= (y >>> 18);
// ---- End Mersenne Twister Algorithm ----
return (y >>> (32 - bits));
}
// This is a fairly obscure little code section to pack a
// byte[] into an int[] in little endian ordering.
/**
* This simply utility method can be used in cases where a byte
* array of seed data is to be used to repeatedly re-seed the
* random number sequence. By packing the byte array into an
* integer array first, using this method, and then invoking
* setSeed() with that; it removes the need to re-pack the byte
* array each time setSeed() is called.
* <p>
* If the length of the byte array is not a multiple of 4 then
* it is implicitly padded with zeros as necessary. For example:
* <pre> byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }</pre>
* becomes
* <pre> int[] { 0x04030201, 0x00000605 }</pre>
* <p>
* Note that this method will not complain if the given byte array
* is empty and will produce an empty integer array, but the
* setSeed() method will throw an exception if the empty integer
* array is passed to it.
*
* @param buf The non-null byte array to be packed.
* @return A non-null integer array of the packed bytes.
* @throws NullPointerException if the given byte array is null.
*/
public static int[] pack(byte[] buf) {
int k, blen = buf.length, ilen = ((buf.length + 3) >>> 2);
int[] ibuf = new int[ilen];
for (int n = 0; n < ilen; n++) {
int m = (n + 1) << 2;
if (m > blen) m = blen;
for (k = buf[--m] & 0xff; (m & 0x3) != 0; k = (k << 8) | buf[--m] & 0xff)
;
ibuf[n] = k;
}
return ibuf;
}
/**
* 使用/验证代码
*/
public static void main(String[] args) {
// MT19937 随机算法的 Java 实现
MTRandom mtRandom = new MTRandom();
Map<Integer, Integer> map = new HashMap<>();
// 循环次数
int times = 1000000;
long startTime = System.currentTimeMillis();
for (int i = 0; i < times; i++) {
// 使用 Map 去重
map.put(mtRandom.next(32), 0);
}
// 打印循环次数
System.out.println("times:" + times);
// 打印 Map 的个数
System.out.println("num:" + map.size());
// 打印非重复比率
System.out.println("proportion:" + map.size() / (double) times);
// 花费的时间(单位为毫秒)
System.out.println("time:" + (System.currentTimeMillis() - startTime));
}
}
# 自定义全局时钟源
CurrentTimeMillisClock.java
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class CurrentTimeMillisClock {
private final AtomicLong now;
private CurrentTimeMillisClock() {
this.now = new AtomicLong(System.currentTimeMillis());
scheduleTick();
}
private void scheduleTick() {
new ScheduledThreadPoolExecutor(1, runnable -> {
Thread thread = new Thread(runnable, "current-time-millis");
thread.setDaemon(true);
return thread;
}).scheduleAtFixedRate(() -> {
now.set(System.currentTimeMillis());
}, 1, 1, TimeUnit.MILLISECONDS);
}
public long now() {
return now.get();
}
public static CurrentTimeMillisClock getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final CurrentTimeMillisClock INSTANCE = new CurrentTimeMillisClock();
}
// ==================================== test =====================================
/**
* 理论上来说,都是调用 100 次 System.currentTimeMillis() ,但是并行情况下比串行情况下要慢上很多(几百上千倍的差距),原因在于:
*
* 1. 调用 gettimeofday() 需要从用户态切换到内核态;
* 2. gettimeofday() 的表现受 Linux 系统的计时器(时钟源)影响,在 HPET 计时器下性能尤其差;
* 3. 系统只有一个全局时钟源,高并发或频繁访问会造成严重的争用。
*
* javaTimeMillis() 方法,这就是 System.currentTimeMillis() 的 native 实现。gettimeofday() 是其方法中的核心代码。
*
* 最常见的办法是用单个调度线程来按毫秒更新时间戳,相当于维护一个全局缓存。其他线程取时间戳时相当于从内存取,不会再造成时钟资源的争用,代价就是牺牲了一些精确度。
* 使用的时候,直接 CurrentTimeMillisClock.getInstance().now()就可以了。
* 不过,在 System.currentTimeMillis() 的效率没有影响程序整体的效率时,就完全没有必要做这种优化,这只是为极端情况准备的。
*/
private static final int COUNT = 10000000;
public static void main(String[] args) throws Exception {
long beginTime = System.nanoTime();
for (int i = 0; i < COUNT; i++) {
System.currentTimeMillis();
}
long elapsedTime = System.nanoTime() - beginTime;
// 单线程调用 100 次
System.out.println("串行 : " + elapsedTime + " ns");
// ------------------------------------------------------------------
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch endLatch = new CountDownLatch(COUNT);
for (int i = 0; i < COUNT; i++) {
new Thread(() -> {
try {
startLatch.await();
// System.currentTimeMillis();
CurrentTimeMillisClock.getInstance().now();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
endLatch.countDown();
}
}).start();
}
beginTime = System.nanoTime();
startLatch.countDown();
endLatch.await();
elapsedTime = System.nanoTime() - beginTime;
// 100 个线程各自调用过一次
System.out.println("并行 : " + elapsedTime + " ns");
}
}
# Hsah 环
也被称为一致性 Hash 算法
ConsistentHashMap.java
/**
* 重点:
* 1. 如何造一个 hash 环,
* 2. 如何在哈希环上映射服务器节点,
* 3. 如何找到对应的节点
*/
public class ConsistentHashMap<K, V> {
//key表示服务器的hash值,value表示服务器
private SortedMap<K, V> sortedMap;
public ConsistentHashMap() {
this.sortedMap = new TreeMap<>();
}
public void put(K key, V value) {
sortedMap.put(key, value);
}
public V get(K key) {
long hash = key.hashCode();
// 得到大于该Hash值的所有Map
SortedMap<K, V> subMap = sortedMap.tailMap(key);
return subMap.isEmpty()
? sortedMap.get(sortedMap.firstKey()) // 如果没有比该key的hash值大的,则从第一个node开始
: subMap.get(subMap.firstKey()); // 第一个Key就是顺时针过去离node最近的那个结点
}
public static void main(String[] args) {
ConsistentHashMap<Integer, String> hashMap = new ConsistentHashMap<>();
for (int i = 0; i < 100; i += 10)
hashMap.put(i, String.valueOf(i));
for (int i = 0; i < 100; i++)
System.out.println(i + "被路由到结点[" + hashMap.get(i) + "]");
}
}
# Spring ApplicationContextHolder
方案一:利用 @Autowired
@Component
public class ApplicationContextHolder {
private static ApplicationContext APPLICATION_CONTEXT;
// 构造注入
public ApplicationContextHolder(ApplicationContext context) {
APPLICATION_CONTEXT = context;
}
public static ApplicationContext getApplicationContext() {
return APPLICATION_CONTEXT;
}
/*
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) APPLICATION_CONTEXT.getBean(name);
}
public static <T> T getBean(Class<T> requiredType) {
return APPLICATION_CONTEXT.getBean(requiredType);
}
*/
}
方案二:实现 ApplicationContextAware 接口:
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/*
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
*/
}