美文网首页
多线程造成的潜在风险

多线程造成的潜在风险

作者: _浅墨_ | 来源:发表于2019-01-08 21:53 被阅读22次

    工具类上传图片到阿里云 OSS 方法如下:

    public class UploadOSSService implements Runnable{
    
        public static final Logger LOGGER = Logger.getLogger(UploadOSSService.class);
        
        private String orderId;
        
        public String getOrderId() {
            return orderId;
        }
    
        public void setOrderHeadId(String orderId) {
            this.orderId = orderId;
        }
    
        @Override
        public void run() {
            
            try {
                // redcoeByte 为图片二维码流,根据 orderId 查询获得
                InputStream in = new ByteArrayInputStream(redcoeByte);
                // 上传到OSS
                String fileName = Md5Encrypt.md5(orderId.toString()) + ".png";
                boolean ifSuccess = MFOSSClientUtil.uploadFile(fileName, in);
                LOGGER.error("upload file to OSS is success or not: " + ifSuccess);
            }catch(Exception e){
                LOGGER.error("上传OSS失败*******: ", e);
            }
    
        }
        
    }
    

    上传下载工具类:

    public class MFOSSClientUtil {
    
        private static final Logger LOGGER = Logger.getLogger(CHOSSClientUtil.class);
        
        /**
         * 上传文件流到阿里云OSS
         * @param filename
         * @param input
         * @return 是否上传成功
         * @throws FileUploadException
         * @throws SocketException
         * @throws IOException
         */
        public static boolean uploadFile(String filename,InputStream input) throws FileUploadException, SocketException, IOException{
            
            boolean success = false;
            // 上传到阿里云OSS
            LOGGER.info("上传图片到阿里云OSS Started");
            String bucketName = ProjectConfig.getOssBucketName();
            OSSClient ossClient = initOSSClient();
            try {   
                // 把字符串存入OSS,Object的名称为firstKey。详细请参看“SDK手册 > Java-SDK > 上传文件”。
                // 链接地址是:https://help.aliyun.com/document_detail/oss/sdk/java-sdk/upload_object.html?spm=5176.docoss/user_guide/upload_object
                // InputStream is = new ByteArrayInputStream("Hello OSS".getBytes());
                ossClient.putObject(bucketName, filename, input);
                success = true;
                System.out.println("Object:" + filename + "存入OSS成功。");
                
            } catch (OSSException oe) {
                oe.printStackTrace();
                LOGGER.error("上传图片到阿里云OSS 失败: "+oe);
            } catch (ClientException ce) {
                ce.printStackTrace();
                LOGGER.error("上传图片到阿里云OSS 失败: "+ce);
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.error("上传图片到阿里云OSS 失败: "+e);
            } finally {
                ossClient.shutdown();
            }
            LOGGER.error("上传图片到阿里云OSS Completed");
            
            return success;
        }
        
        
        /**
         * 下载文件
         * 
         * @param fileName
         */
        public static void download(String fileName,OutputStream output) throws FileUploadException, SocketException, IOException{
    
            LOGGER.error("Calling download...");
            OSSClient ossClient = initOSSClient();
            String bucketName = ProjectConfig.getOssBucketName();
            try {
                OSSObject ossObject = ossClient.getObject(bucketName, fileName);
                InputStream inputStream = ossObject.getObjectContent();
                try {
                        int bufSize = 1024 * 4;
                        byte[] buffer = new byte[bufSize];
                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            output.write(buffer, 0, bytesRead);
                        }
                        
                 } catch (IOException e) {
                    LOGGER.error("下载阿里云OSS 图片,读取 ossObject 数据流到 outputStream 失败:" + e);
                    e.printStackTrace();    
                 }
            } catch (Exception e) {
                LOGGER.error("下载阿里云OSS 图片失败:" + e);
                e.printStackTrace();
            } finally {
                ossClient.shutdown();
            }
        }
        
        
        /**
         * 初始化 OSSClient
         * @return
         * @throws SocketException
         * @throws IOException
         */
        public static OSSClient initOSSClient() throws SocketException, IOException
        {
            // OSS信息
            String endpoint = "";
            String accessKeyId = "";
            String accessKeySecret = "";
            String bucketName = "";
            
            // 生成OSSClient,您可以指定一些参数,详见“SDK手册 > Java-SDK > 初始化”,
            // 链接地址是:https://help.aliyun.com/document_detail/oss/sdk/java-sdk/init.html?spm=5176.docoss/sdk/java-sdk/get-start
            OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
            try {   
                    // 判断Bucket是否存在。详细请参看“SDK手册 > Java-SDK > 管理Bucket”。
                // 链接地址是:https://help.aliyun.com/document_detail/oss/sdk/java-sdk/manage_bucket.html?spm=5176.docoss/sdk/java-sdk/init
                if (ossClient.doesBucketExist(bucketName)) {
                    System.out.println("您已经创建Bucket:" + bucketName + "。");
                } else {
                    System.out.println("您的Bucket不存在,创建Bucket:" + bucketName + "。");
                    // 创建Bucket。详细请参看“SDK手册 > Java-SDK > 管理Bucket”。
                    // 链接地址是:https://help.aliyun.com/document_detail/oss/sdk/java-sdk/manage_bucket.html?spm=5176.docoss/sdk/java-sdk/init
                    ossClient.createBucket(bucketName);
                }
            } catch (OSSException oe) {
                oe.printStackTrace();
                LOGGER.error("初始化阿里云OSS 失败: "+oe);
            } catch (ClientException ce) {
                ce.printStackTrace();
                LOGGER.error("初始化阿里云OSS 失败: "+ce);
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.error("初始化阿里云OSS 失败:"+e);
            } 
            return ossClient;
        }
    }
    

    现在问题来了,当使用下面方法调用上传时,如果是批量上传,由于共用 uploadOSSService,可能造成 orderId 与上传的图片不匹配。单独上传就没问题。因为批量上传时候,有可能另外线程修改了 orderId ,但还没来得及修改图片流,就执行上传成功了。从而造成 orderId 和图片不对应。

    解决方法是不要 Autowired UploadOSSService,而是每次调用的时候 new 一个新 UploadOSSService 对象。

    @Autowired
    private UploadOSSService uploadOSSService;
    
    //上传到OSS。单独起线程处理
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
    uploadOSSService.setOrderId(orderId);
    fixedThreadPool.execute(uploadOSSService);
    

    涉及多线程的时候,一定要异常小心。

    相关文章

      网友评论

          本文标题:多线程造成的潜在风险

          本文链接:https://www.haomeiwen.com/subject/vpdvrqtx.html