在netty中的singleton类中使用threadlocal

时间:2016-12-20 06:15:25

标签: java multithreading netty

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

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

  2. 在完成处理之前,requestE来了,nio-thread1来处理requestE,并将threadLocal值设置为e

  3. 那么,在这种情况下,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;
        }
    }
    

    我用春天

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

    获得课程。

1 个答案:

答案 0 :(得分:1)

如果已知此类代码被同一线程内的多个租户使用,则在这种情况下使用threadlocal可能会非常危险。虽然我们有一个替代方案,可以使它更容易管理。 而不是private ThreadLocal<byte[]> key = new ThreadLocal<byte[]>();,您可以使用

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

然后无论何时想要获取threadLocal,都可以使用相应的对象访问它。如果您知道要为每个请求创建不同的实例,则可以使用this作为密钥,否则您可以使用Request之类的任何其他信息来根据请求映射密钥。

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