在 netty 的单例中使用 threadlocal class

Using threadlocal in a singleton class in netty

我不确定是否存在任何线程安全问题 因为我用的是singleton util class 并且有一个成员变量, 起初,我使用 threadlocal 来尝试避免这些问题。但是netty的Nio线程池太小了(大小只有4,导致cpu核心是2),所以我想知道在并发级别高的情况下会存在一些线程安全问题,因为例子:

  1. nio-thread1正在处理requestA,设置threadLocal值为a

  2. 还没处理完requestE,nio-thread1来处理requestE,设置threadLocal值为e

那么,在这种情况下,requestA 是否受到影响?如果是,如果我想将此值保留为成员变量(而不是将其放入方法),我该如何避免它

感谢任何建议!

这是我的代码:

/**
 *
 * @param <T>
 *            source
 * @param <V>
 *            result
 * @param <K>
 *            key
 */
public interface BaseDecryption<S, R, K> {

        public static enum DecryType {
            AES128CBC, AES128XOR, XOR
        }

        public BaseDecryption<S, R, K> withDecryType(DecryType type);

        public DecryType getDecryType();

        public R decrypt(S source);

    }


public abstract class BytesDecryption implements
            BaseDecryption<byte[], byte[], byte[]> {

        private DecryType decrypTye;

        /**
         * Here is where I used the treadLocal
         * 
         */
        private ThreadLocal<byte[]> key = new ThreadLocal<byte[]>();

        protected DecryType getDecrypTye() {
            return decrypTye;
        }

        protected byte[] getKey() {
            return this.key.get();
        }

        public BaseDecryption<byte[], byte[], byte[]> withDecryKey(byte[] key) {
            this.key.set(key);
            return this;
        }

        @Override
        public BaseDecryption<byte[], byte[], byte[]> withDecryType(
                DecryType decryType) {
            this.decrypTye = decryType;
            return this;
        }

    }

@Component("LEAD_AES128CBC")
public class AES128CBC extends BytesDecryption {

    private AlgorithmParameters params;
    private static final String KEY_ALGORITHM = "AES";
    public static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
    private static final Logger logger = LoggerFactory
            .getLogger(AES128CBC.class);

    public AES128CBC() throws NoSuchAlgorithmException,
            InvalidParameterSpecException {
        Security.addProvider(new BouncyCastleProvider());
        this.withDecryType(DecryType.AES128CBC);
        initVi();
    }

    @Override
    public byte[] decrypt(byte[] source) {
        byte[] key = getKey();
        byte[] size16Key = new byte[16];
        System.arraycopy(key, 0, size16Key, 0, 16);
        SecretKey secretKey = new SecretKeySpec(size16Key, KEY_ALGORITHM);
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, params);
            if (source.length % 16 != 0) {
                byte[] encryptionBytes = new byte[source.length - source.length
                        % 16];
                System.arraycopy(source, 0, encryptionBytes, 0,
                        encryptionBytes.length);
                byte[] decryptionBytes = cipher.doFinal(encryptionBytes);
                byte[] finalBytes = new byte[decryptionBytes.length
                        + source.length % 16];
                System.arraycopy(decryptionBytes, 0, finalBytes, 0, 0);
                // only multiple of 16 bytes will be decrypted, so copy the
                // remained
                System.arraycopy(source, encryptionBytes.length, finalBytes,
                        encryptionBytes.length, source.length % 16);
                return finalBytes;
            }
            return cipher.doFinal(source);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException
                | IllegalBlockSizeException | BadPaddingException
                | InvalidKeyException | InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public DecryType getDecryType() {
        return DecryType.AES128CBC;
    }

    public void initVi() throws NoSuchAlgorithmException,
            InvalidParameterSpecException {
        byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        params = AlgorithmParameters.getInstance(KEY_ALGORITHM);
        params.init(new IvParameterSpec(iv));
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                                 + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }
}

我用spring

BeanUtil.getBean(encryType) // encryType may equal to LEAD_AES128CBC

获得 class.

如果已知此类代码被同一线程中的多个租户使用,则在此类情况下使用 threadlocal 可能会非常危险。尽管我们有一个替代方案,可能会使它更容易管理。 您可以使用

而不是 private ThreadLocal<byte[]> key = new ThreadLocal<byte[]>();
`private ThreadLocal<Map<Object, byte[]>> key = new ThreadLocal<Map<Object, byte[]>>();

然后只要你想获取threadLocal,你就可以访问相应的对象。如果您知道要为每个发出的请求创建不同的实例,则可以使用 this 作为您的密钥,否则您可以使用任何其他信息,如 Request 将密钥映射到请求。

当然,您可以选择使用静态 Map 而不是 threadlocal。希望这有帮助。