并发编程第14篇,并发队列框架实战操作
并发队列框架实战操作
用户态与内核态之间的区别
内核态(Kernel Mode):运行操作系统程序,操作硬件Cpu的调度分配、内存、IO操作。
用户态(User Mode):运行用户程序
Synchronized升级重量锁优化过程
Synchronized 锁 默认是为轻量锁,慢慢升级为重量级锁,当如果线程没有获取锁的时候
会被阻塞等待,同时释放cpu执行权,而我们阻塞或者唤醒线程的过程是有系统内核实现的,所以如果Synchronized 升级为重量锁的时候是一个用户态切换为内核态过程,效率非常低。
异步打印日志的需求
在高并发的情况下,服务器端频繁的接收到请求,这时候因为打印日志本身操作是需要对IO做写的操作,写的操作操作有可能会暂停到打印日志的线程。
用户空间需要切换到内核空间,会导致用户空间阻塞的。
异步日志框架源码分析
AsyncAppender中AsyncAppenderBase类, 创建了一个Worker线程异步的从队列中获取日志,并且异步写入到硬盘中。
基于ArrayBlockingQueue手写异步框架
public class LogBlockingQueue { private static final int maxCapacity = 20000; private static ArrayBlockingQueue<String> logBlockingQueue = new ArrayBlockingQueue<String>(maxCapacity); /** * 添加log * * @param log * @return */ public static boolean addLog(String log) { return logBlockingQueue.offer(log); } /** * 查询log * * @param * @return */ public static String getLog() { return logBlockingQueue.poll(); } }
|
/** * 异步写日志文件 */ public class LogException { private static final String path = "e://code/abc.log"; public static void writeErrorMsg(String content) { writeErrorMsg(path, content); } /** * @param path * @throws IOException * @将错误信息输入到txt中 */ public static void writeErrorMsg(String path, String content) { File F = new File(path); //如果文件不存在,就动态创建文件 if (!F.exists()) { try { F.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } FileWriter fw = null; String writeDate = "时间:" + getNowDate() + "---" + "error:" + content; try { //设置为:True,表示写入的时候追加数据 fw = new FileWriter(F, true); //回车并换行 fw.write(writeDate + "\r\n"); } catch (IOException e) { e.printStackTrace(); } finally { if (fw != null) { try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * @return * @获取系统当前时间 */ public static String getNowDate() { Calendar D = Calendar.getInstance(); int year = 0; int moth = 0; int day = 0; year = D.get(Calendar.YEAR); moth = D.get(Calendar.MONTH) + 1; day = D.get(Calendar.DAY_OF_MONTH); String nowDate = String.valueOf(year) + "-" + String.valueOf(moth) + "-" + String.valueOf(day); return nowDate; } public static void main(String[] args) { writeErrorMsg("abc"); } }
|
@Component @Slf4j public class LogMonitor { public void start() { log.info("开启异步采集日志,存储到硬盘中.."); new Worker().start(); } class Worker extends Thread { @Override public void run() { for (; ; ) { String mtLog = LogBlockingQueue.getLog(); if (!StringUtils.isEmpty(mtLog)) { LogException.writeErrorMsg(mtLog); log.info("异步的将mtLog:{},写入到银盘中.", mtLog); } } } } }
|
@Slf4j @Component public class StartListener implements ApplicationListener<ApplicationReadyEvent> { @Autowired private LogMonitor logMonitor; @Override public void onApplicationEvent(ApplicationReadyEvent event) { log.info(">>项目启动成功<<<"); logMonitor.start(); } }
|
并发编程课程总结
如何优化多线程
- 锁建议做好使用CAS或者自旋锁 ,不建议为悲观锁
- Synchronized锁使用偏向锁或者轻量锁 减少锁持有时间
- 降低锁的粒度采用 分段锁
- 使用fork jojn 并行提高多线程的效率
- 减少多线程上下文的切换 使用多核处理器或者是线程池
本章主要说并发队列都运用,并发队列加netty还能做消息中间件,那么并发队列都底层又是怎么实现都呢?
每一行代码都有它的涵义,多问一句为什么;别怕,理清思路,一切代码都是数据的流动和转化,耐心一点,慢慢积累!一起加油!!!