namenode双缓冲写入数据

直接贴代码

package com.asiainfo.custom.doublebuff;

import java.util.LinkedList;

/**
 * 总结,采取了分段锁,将(获取事务id->写内存数据)(刷盘)进行分段锁。就是说,
 * 多线程顺序写数据。使得可以多线程批量刷数据。
 * 如果不使用双缓冲,那么读写就只能在一块内存中
 * 如果不使用分段锁,那么就只能串行刷新一条数据。
 */

public class DoubleBuff {

    public LinkedList<EditLog> currentMemory = new LinkedList<>();

    public LinkedList<EditLog> buffReady = new LinkedList<>();

    public void write(EditLog editLog) {
        currentMemory.add(editLog);
    }

    public void exChangeMemory() {
        LinkedList<EditLog> tmp = null;
        tmp = this.currentMemory;
        this.currentMemory = this.buffReady;
        this.buffReady = tmp;
    }

    public void flush() {
        for (EditLog editLog : buffReady) {
            //模拟将数据写到磁盘
            System.out.println(editLog);
        }
        buffReady.clear();
    }

    public int getMaxTxid() {
        return buffReady.getLast().getTxid();
    }
}

class LogManager {

    private DoubleBuff doubleBuff = new DoubleBuff();
    private int txid = 0;

    private ThreadLocal<Integer> curPro = new ThreadLocal<>();

    //需要通过这个方法写内存.
    //这个方法是多线程调用的。
    public void logEdit(String logContent) throws InterruptedException {
        synchronized (this) { //锁一
            EditLog editLog = new EditLog(logContent, txid);
            //多线程调用的,所以这个++ 就肯定会出现加错的情况了
            txid++;
            //把当前现成,跟事务id绑定在一起;现成A->1 B->2 C->3
            curPro.set(txid);

            doubleBuff.write(editLog);
        }
        //这个操作是非常耗时的,上面的write又很快,所以会有大量的线程都聚集到这个flush中
        logFlush();

    }

    //false代表正在刷盘
    private boolean isSyncRunning = false;
    private int maxTxid = 0;
    private boolean isWait = false;

    private void logFlush() throws InterruptedException {
        /* thinking...
         * 交换内存是需要线程安全的操作,所以这里加锁,不能每个线程都同时交换内存。
         * 但是我们不希望,写内存->交换空间 这两个步骤串行操作。因为如果串行操作了,那么就只会每次
         * 有一条数据写入current 紧接着就开始交换内存。这样效率可能不高
         */
        //所以刷盘的时候,有可能内存里面已经有好多事务的数据了。锁二
        synchronized (this) {
            /* thinking...
             * 这里思考,那我们进来,可以直接就交换内存吗(current里面是有数据的)
             * 显然是不可以的。如果我们交换内存的时候,有线程正在刷盘,那么就会发生错误
             * 所以我们应该找一个标志位,当刷盘结束的时候,改变状态,所以我们设立 isSyncRunning
             */
            //如果正在刷盘,那么就是没操作也不进行
            if (isSyncRunning) {
                /* thinking...
                 * 如果我当前的现成事务id<正在处理的事务id,说明这个数据已经被刷盘了
                 * 由于其他线程率先抢到了锁二,而这个线程的事务日志比当前的大,而却已经刷盘了
                 * 所以当前现成就什么也不需要做了
                 */
                if (curPro.get() < maxTxid) {
                    return;
                }
                /*
                 * 如果有线程睡眠要执行任务了,那么也不需要其他线程处理了,那就退出。
                 */
                if (isWait) {
                    return;
                }
                //进入到这里的代码都是curPro.get()>=maxTxid,说明,当前现成写入的数据还没有被处理
                //那么当前现成是肯定要执行的,那就先给当前现成睡眠,等待刷盘完成

                //但是wait方法会让出锁,所以其他线程也会拿到锁进入这个方法,
                isWait = true;
                while (isSyncRunning) {
                    wait(100);
                }
                isWait = false;

            }
            doubleBuff.exChangeMemory();

            if (doubleBuff.buffReady.size() > 0) {
                maxTxid = doubleBuff.getMaxTxid();
            }

        }
        doubleBuff.flush();
        synchronized (this) {
            isSyncRunning = true;
            this.notify();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值