• Java服务端性能优化


    《Java程序性能优化》说性能优化包含五个层次:设计调优、代码调优、JVM调优、数据库调优、操作系统调优。

    常用的几个代码优化方案:

    使用单例

    对于IO处理、数据库连接、配置文件解析加载等一些非常耗费系统资源的操作,我们必须对这些实例的创建进行限制,或者是始终使用一个公用的实例,以节约系统开销,这种情况下就需要用到单例模式。

    使用Future模式

    假设一个任务执行起来需要花费一些时间,为了省去不必要的等待时间,可以先获取一个“提货单”,即Future,然后继续处理别的任务,直到“货物”到达,即任务执行完得到结果,此时便可以用“提货单”进行提货,即通过Future对象得到返回值。

    public class RealData implements Callable<String> {  
        protected String data;  
    
        public RealData(String data) {  
            this.data = data;  
        }  
    
        @Override  
        public String call() throws Exception {  
            //利用sleep方法来表示真是业务是非常缓慢的  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
            return data;  
        }  
    } 
    
    public class Application {  
        public static void main(String[] args) throws Exception {  
            FutureTask<String> futureTask =   
                    new FutureTask<String>(new RealData("name"));  
            ExecutorService executor =   
                    Executors.newFixedThreadPool(1); //使用线程池  
            //执行FutureTask,相当于上例中的client.request("name")发送请求  
            executor.submit(futureTask);  
            //这里可以用一个sleep代替对其他业务逻辑的处理  
            //在处理这些业务逻辑过程中,RealData也正在创建,从而充分了利用等待时间  
            Thread.sleep(2000);  
            //使用真实数据  
            //如果call()没有执行完成依然会等待  
            System.out.println("数据=" + futureTask.get());  
        }  
    } 

    使用线程池

    合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

    在 Java 5 之后,并发编程引入了一堆新的启动、调度和管理线程的API。Executor 框架便是 Java 5 中引入的,其内部使用了线程池机制,它在 java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。

    public class MultiThreadTest {
        public static void main(String[] args) {
            ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-%d").build();
            ExecutorService executor = new ThreadPoolExecutor(2, 5, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory);
            executor.execute(new Runnable() {
                @Override
                public void run() {
                   System.out.println("hello world !");
                }
            });
            System.out.println(" ===> main Thread! " );
        }
    }

    使用NIO

    JDK自1.4起开始提供全新的I/O编程类库,简称NIO,其不但引入了全新高效的Buffer和Channel,同时,还引入了基于Selector的非阻塞 I/O机制,将多个异步的I/O操作集中到一个或几个线程当中进行处理,使用NIO代替阻塞I/O能提高程序的并发吞吐能力,降低系统的开销。

    对于每一个请求,如果单独开一个线程进行相应的逻辑处理,当客户端的数据传递并不是一直进行,而是断断续续的,则相应的线程需要 I/O等待,并进行上下文切换。而使用NIO引入的Selector机制后,可以提升程序的并发效率,改善这一状况。

    public class NioTest {  
        static public void main( String args[] ) throws Exception {  
            FileInputStream fin = new FileInputStream("c:\test.txt");  
            // 获取通道  
            FileChannel fc = fin.getChannel();  
            // 创建缓冲区  
            ByteBuffer buffer = ByteBuffer.allocate(1024);  
            // 读取数据到缓冲区  
            fc.read(buffer);  
            buffer.flip();  
            while (buffer.remaining()>0) {  
                byte b = buffer.get();  
                System.out.print(((char)b));  
            }  
            fin.close();  
        }  
    }

    锁优化

    在并发场景中,我们的代码中经常会用到锁。存在锁,就必然存在锁的竞争,存在锁的竞争,就会消耗很多资源。那么,如何优化我们Java代码中的锁呢?主要可以从以下几个方面考虑:

    • 减少锁持有时间(可以使用同步代码块来代替同步方法。这样既可以减少锁持有的时间。)
    • 减少锁粒度(要在并发场景中使用Map的时候,记得使用ConcurrentHashMap来代替HashTable和HashMap。)
    • 锁分离(普通锁(如syncronized)会导致读阻塞写、写也会阻塞读,同时读读与写写之间也会进行阻塞,可以想办法将读操作和写操作分离开。)
    • 锁粗化(有些情况下我们希望把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。)
    • 锁消除(锁消除是Java虚拟机在JIT编译是,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过锁消除,可以节省毫无意义的请求锁时间。)

    压缩传输

    在进行数据传输之前,可以先将数据进行压缩,以减少网络传输的字节数,提升数据传输的速度,接收端可以将数据进行解压,以还原出传递的数据,并且,经过压缩的数据还可以节约所耗费的存储介质(磁盘或内存)的空间以及网络带宽,降低成本。当然,压缩也并不是没有开销的,数据压缩需要大量的CPU计算,并且,根据压缩算法的不同,计算的复杂度以及数据的压缩比也存在较大差异。一般情况下,需要根据不同的业务场景,选择不同的压缩算法。

    缓存结果

    对于相同的用户请求,如果每次都重复的查询数据库,重复的进行计算,将浪费很多的时间和资源。将计算后的结果缓存到本地内存,或者是通过分布式缓存来进行结果的缓存,可以节约宝贵的CPU计算资源,减少重复的数据库查询或者是磁盘I/O,将原本磁头的物理转动变成内存的电子运动,提高响应速度,并且线程的迅速释放也使得应用的吞吐能力得到提升

    转载:
    原文地址:人人都能掌握的Java服务端性能优化方案

  • 相关阅读:
    LeetCode 75. Sort Colors(按颜色进行排序)
    LeetCode 451. Sort Characters By Frequency(按照字符出现次数对字符串排序)
    LeetCode 347. Top K Frequent Elements(出现频率最多的 k 个元素)
    LeetCode 215. Kth Largest Element in an Array(数组求第k大)
    CF #629 Div.3 E(LCA)F
    系统函数
    CASE表达式
    循环得出数据库中所有的 DB_ID,DB_NAME
    数据库的编码问题
    检验临时表是否存在
  • 原文地址:https://www.cnblogs.com/aixing/p/13327584.html
Copyright © 2020-2023  润新知