利用java concurrent 包实现日志写数据库的并发处理

一、概述

在很多系统中,往往需要将各种操作写入数据库(比如客户端发起的操作)。

最简单的做法是,封装一个公共的写日志的api,各个操作中调用该api完成自己操作日志的入库。但因为入数据库效率比较低,如果每个操作自己入库,则会影响响应速度。而且当操作并发度很高时,往往同时有多个线程在写数据库,也会对系统有影响。

考虑的解决方案是,这个api并不实际完成入库,而是将每个操作日志信息写到一个公共的缓存中,然后应用系统起了一个独立的线程(一直运行)在后台进行入库。如果当前缓存中有记录,就写库,没有记录,就堵塞住。

这里关键的是要有一个缓存日志的数据结构,我们这里使用java current包中的并发数据结构LinkedBlockingDeque类。

二、具体代码

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;

public class LogManager implements Runnable {
    //定义缓存数据结构,支持并发操作
    private static LinkedBlockingDeque<ActionLog> list = new LinkedBlockingDeque<ActionLog>();
    static{
        //创建并启动线程,该线程实现日志的入库
        new Thread(new LogManager()).start();
    }
    public static void addActionLog(ActionLog log) {
        list.add(log);
    }

    @Override
    public void run() {
        List<ActionLog> items = new ArrayList<ActionLog>();
        while (true) {
            //从缓存中取出1条日志,但不从缓存中删除。用于检查队列中是否有数据
            ActionLog item = list.peek();
            if (item == null && items.size() > 0) {
                //item为null说明缓存中没有日志了,这时如果临时队列items中有记录,就可以批量入库了
                work(items);
            }
            //从缓存中取出并删除最前面的日志,如果无数据,在堵塞
            ActionLog result = deleteItem();
            //将取出的记录放到临时队列中
            items.add(result);
        }
    }

    private ActionLog deleteItem() {
        ActionLog result = null;
        try {
            //取出并删除最前面的一条数据,如果缓存中无数据,则堵塞住
            result = list.takeFirst();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }

    private void work(List<ActionLog> items) {
        //TODO 完成入库。这里代码没有提供
        //入库后删除临时队列中记录
        items.clear();
    }

}

各种工作线程调用 addActionLog 方法将要入库的日志放到缓存中。由LogManager通过自己的线程控制入库。

三、小结

上述代码虽然简单,但很好的解决了前面提出的问题。不过这个解决方案存在一个问题。

如果说系统产生日志的速度超过了单个线程入库的速度,上述代码就有问题。就会造成该线程不停地入库。

这时就需要考虑增加额外的处理速度,如增加入库线程。

所以上述代码,只适合并发日志量不是特别大的情况下的场景。

时间: 2024-10-07 17:57:55

利用java concurrent 包实现日志写数据库的并发处理的相关文章

带你玩转java多线程系列 “道篇” 多线程的优势及利用util.concurrent包测试单核多核下多线程的效率

java多线程 “道篇” - 多线程的优势及用concurrent包测试单核多核下多线程的效率 1 超哥对于多线程自己的理解 2 测试代码 3 CountDownLatch这个同步辅助类科普 4 如何把电脑设置成单核 5 测试结果 1 超哥对于多线程自己的理解 超哥的理解:对于多线程,无非是对于顺序执行下任务的一种抽取和封装,将原来顺序执行的任务单独拿出来放到线程类的run方法中,通过线程类的start方法进行执行,对于多线程访问共同资源时,我们需要加锁,也就是只有某个线程在拥有锁的时候,才能够

Java网络编程:利用Java mail包发送电子邮件

下面代码是利用Java mail包封装了一个发送邮件的类 import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.mail.Me

总结了一下网上MySQL 5.6.26 利用tar.gz包安装的MysSQL数据库会遇到的问题

总结了一下网上MySQL 5.6.26 利用tar.gz包安装的MysSQL数据库会遇到的问题     安装包: Linux Generic mysql-5.6.26-linux-glibc2.5-x86_64.tar.gz 安装环境: CentOS 7 这个数据库照理说应该没什么难度,但是还是纠结了两天.中文乱码问题,数据库服务restart报缺失.pid文件问题.处理过程在这里做个小的总结,以便以后工作中需要方便查阅. 1.创建mysql组和mysql用户: [[email protecte

java concurrent包介绍及使用

说一说java的concurrent包1-concurrent包简介 前面一个系列的文章都在围绕hash展开,今天准备先说下concurrent包,这个系列可能会以使用场景说明为主,concurrent包本身的代码分析可能比较少: 我在这方面的实践经验较为有限,有错误欢迎批评指正 不过前一个系列并未结束,还有一些文章没有放出来,欢迎关注核桃博客 concurrent包是jdk1.5引入的重要的包,主要代码由大牛Doug Lea完成,其实是在jdk1.4时代,由于java语言内置对多线程编程的支持

java concurrent包的实现原理

由于java的CAS同时具有 volatile 读和volatile写的内存语义,因此Java线程之间的通信现在有了下面四种方式: A线程写volatile变量,随后B线程读这个volatile变量. A线程写volatile变量,随后B线程用CAS更新这个volatile变量. A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量. A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量. Java的CAS会使用现代处理器上提供的

java concurrent包常用类小结

concurrent包是常用多线程的相关包,最近由于开发sdn程序,对于多线程使用比以前多了很多,现简单总结下. 第一类  原子类:用在多个线程共同操作一个计数的情况 AtomicLong AtomicInteger 第二类 lock和condition condition是从lock中得到的,所以在使用时,在执行了lock.lock()后才进行condition的操作,condition常用的两个方法await和signal. 常用在多个线程操作一个共同的资源,一个线程执行结束后,另一个线程才

Java并发编程系列 concurrent包概览

从JDK 1.5开始,增加了java.util.concurrent包,concurrent包的引入大大简化了多线程程序的开发. 查看JDK的API可以发现,java.util.concurrent包分成了三个部分,分别是java.util.concurrent.java.util.concurrent.atomic和java.util.concurrent.lock. >>Atomic包 API中的说明是“A small toolkit of classes that support loc

利用JAVA线程安全队列简单实现读者写者问题。

常见的操作系统教科书中,会使用互斥锁来实现读者线程和写者线程的同步问题,但是在JDK5推出线程安全队列之后,将该问题变得异常简单. java.util.concurrent.ConcurrentLinkedQueue 是线程安全的非阻塞队列,其实很容易想到,非阻塞队列当线程需要等待的时候,则不会阻塞等待,而是直接根据情况返回. java.util.concurrent.LinkedBlockingQueue 是线程安全的阻塞队列,该队列能够在很多情况下对线程进行阻塞,比如队列为空时调用take(

《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecut