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

多线程造成的潜在风险

作者: _浅墨_ | 来源:发表于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