Java常用编码套路

前因

最近线上环境经常出现OOM,导致系统崩溃,在排查过程中,发现代码在使用多线程时有很多不规范的地方,导致内存泄露,因此我决定整理出一些常用的示例,在使用时 直接套用即可。因个人技术水平有限,如有错误,望指正。

宗旨

代码可以不是最高效和最简洁的,但一定会是最安全和清晰明了的。

1. 流什么情况下需要手动关闭

为什么要关闭流

一个对象在没有被引用变量指向的时候它会变成垃圾,最终会被垃圾回收器从内存中清除,对于我们创建的流对象,干嘛还要"调用close方法将其进行关闭呢,以释放与其相关的资源"呢?
当我们在程序中创建一个IO流对象的时候,同时系统也会创建一个叫做流的东西,在这种情况下,计算机内存中实际产生了两个事物,一个是java程序中类的实例对象,一个是系统本身产生的某种资源,而java垃圾回收器只能管理程序
中类的实例对象,没办法去管理系统产生的资源,所以程序需要调用close方法,去通知系统释放其自身产生的资源。

什么情况下需要手动关闭流

  1. 指向内存的流可以不用关闭,指向存储卡/硬盘的流一定要关闭!
    如果是内存读写流,它内部是使用字节数组读内存的,这个字节数组是它的成员变量,当这个数组不再使用变成垃圾的时候,Java的垃圾回收机制会将它回收。所以不需要关流。 如:ByteArrayOutputStream或ByteArrayInputStream
    在这里插入图片描述

如果是指向存储卡/硬盘的流,因为系统会产生流,如果不关闭则会导致内存泄露。如:FileOutputStream。

  1. JDK1.7之后 很多流都继承了 Closeable或AutoCloseable 接口,如果继承了,则无需手动关闭。 因为 Closeable 也继承了 AutoCloseable 接口。如果继承了 Closeable 也间接继承了 AutoCloseable 接口。

AutoCloseable接口位于java.lang包下,从JDK1.7开始引入。可以针对于所有实现该接口的流,而closable本身也实现了该接口,java的io流间接性的可以自动关闭接口,也就是说从jdk1.7开始,不需要手动去关流。
在这里插入图片描述

在这里插入图片描述

如何正确关闭流

JDK1.7之前 try{} finally{} 在finally中释放资源。

	
        FileInputStream fis = null;
		FileOutputStream  fos = null;
		try {
			fis = new FileInputStream("xxx.txt");
			fos = new FileOutputStream("kkk.txt");
			int len = -1;
			while((len=fis.read())!=-1) {
				fos.write(len);
			}
		} finally {
			try {
				if (fis!=null) 
					fis.close();
			} finally {
				if (fis!=null) 
					fos.close();
			}
		}

JDK1.7之后采用 {try}-with-resources的解释,将可能抛出异常的代码块放入到try块中,在try结束的时候,会自动将这些资源关闭(调用close方法)。

		try(
				FileInputStream fis = new FileInputStream("xxx.txt");
				FileOutputStream fos = new FileOutputStream("hhh.txt");
				MyAutoClosable closable = new MyAutoClosable();
		){
			int len = -1;
			while((len=fis.read())!=-1) {
				fos.write(len);
			}
		}

注意:这二者 try 后面紧跟符号不一样!!!!

建议

只要是流,用完后都在finally块里调用其close()方法进行关闭。万一你记混了,认为不需要close,项目上生产环境出了问题,相信我,你会哭的哦-。-

2. Dubbo 项目内部 引用 同项目的Service 用 @Reference 还是 @Autowired

3. 异步调用

        CountDownLatch countDownLatch = new CountDownLatch(completeTaskDTO.getTaskIds().size());
        // 因为事务在call方法中,所以如果线程这个方法被中断,可能导致当前任务的事务不会回滚
        List<Future<Result<Boolean>>> futures = new ArrayList<>();
        Callable<Result<Boolean>> callable = null;

        for (Long taskId : completeTaskDTO.getTaskIds()) {
            try {
                // 调用内部类
                callable = new CompleteUserTaskAsync(taskId);
                Future<Result<Boolean>> submit = visiableThreadPoolTaskExecutor.submit(callable);
                futures.add(submit);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }
        List<Result<Boolean>> responseResults = new ArrayList<>();
        for (Future<Result<Boolean>> result : futures) {
            while (true) {
                //CPU高速轮询:每个future都并发轮循,判断完成状态然后获取结果,这一行,是本实现方案的精髓所在。即有10个future在高速轮询,完成一个future的获取结果,就关闭一个轮询
                if (result.isDone() && !result.isCancelled()) {
                    //获取future成功完成状态,如果想要限制每个任务的超时时间,取消本行的状态判断+future.get(1000*1, TimeUnit.MILLISECONDS)+catch超时异常使用即可。
                    try {
                        Result<Boolean> responseResult = result.get();//获取结果
                        responseResults.add(responseResult);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    }
                    //当前future获取结果完毕,跳出while
                    break;
                } else {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        try {
            countDownLatch.await();
            System.out.print("all thread task have finished,main thread will continue run");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

// 内部类
 class CompleteUserTaskAsync implements Callable<Result<Boolean>> {

        private Long taskId;
        private CompleteUserTaskAsync(Long taskId) {
            this.taskId = taskId;
        }

        @Override
        public Result<Boolean> call() throws Exception {
           return Result.responseSuccess(Boolean.TRUE);
    }
  1. ReentrantLock 用法

     *    ReentrantLock lock = new ReentrantLock();
     *     lock.lock();
     *     try {
     *       // ... method body
     *     } finally {
     *       lock.unlock();
     *     }
  

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风中思絮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值