java双缓冲池

合集下载

Ehcache 2 缓存区配置详解

Ehcache 2 缓存区配置详解

Ehcache 2 缓存区配置详解第1章Ehcache简介EhCache是一个开放源码的,基于标准的高速缓存系统。

Ehcache可以显著提高应用性能,降低数据库负载,简化应用扩展。

Ehcache健壮、功能齐全,也历经了众多应用考验,使得它成为使用最广泛的基于Java的缓存系统。

Ehcache可以支持从进程内的一个或多个节点的部署方式到进程内与进程外混合、高达TB 大小的高速缓存。

Ehcache目前由Terracotta公司维护,使用Apache 2 许可证。

Ehcache截止目前最新版本为2.6。

支持多种方式缓存:●Standalone模式。

嵌入应用进程内。

单点,多节点间无沟通。

●Replication模式。

嵌入应用内部,通过RMI或JGroup或JMS进行节点同步。

●Cache Server模式。

作为独立缓存服务器,提供REST与WebService接口供访问。

●Distributed Caching模式。

采用Terracotta Server Array实现高可用的分布式缓存。

Standalone与Replication均是较传统的使用方式,很多场景下难以满足动态基础设施环境下应用弹性的要求。

Cache Server使得缓存服务可以容易的进行水平扩展,但是基于REST与WebService的访问方式降低了缓存访问的效率,不太适合对缓存实时性要求较高的场景。

Distributed Caching模式提供了进程内与进程间缓存较理想的结合模式,支持水平扩展,高可用,对网络依赖程度较低,可以很好适应应用弹性伸缩,是动态基础设施条件下较理想的缓存模式。

第2章Ehcache 2 缓存区配置Ehcache默认配置文件在Ehcache客户端的classpath根目录下,名为ehcache.xml。

典型的配置文件如下:<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="ehcache.xsd"updateCheck="false" monitoring="autodetect"dynamicConfig="true" name="example"><defaultCachemaxElementsInMemory="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"overflowToDisk="true"diskSpoolBufferSizeMB="30"maxElementsOnDisk="10000000"diskPersistent="false"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"/><cache name="colors"maxElementsInMemory="100"maxElementsOnDisk="0"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="0"memoryStoreEvictionPolicy="LFU"><terracotta/></cache><terracottaConfig url="localhost:9510"/></ehcache>缓存区使用<cache/>标签进行配置,主要属性以及意义如下:⏹name(必填属性):缓存区名称,用以区别缓存区,必须唯一。

如何实现双缓冲

如何实现双缓冲

如何实现双缓冲双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。

双缓冲实现过程如下:1、在内存中创建与画布一致的缓冲区2、在缓冲区画图3、将缓冲区位图拷贝到当前画布上4、释放内存缓冲区(1)在内存中创建与画布一致的缓冲区CDC dc;//这是窗口的DC,假设已加载好CDC MemDC; //创建内存中的一个临时dc- MemDC, MemDC用来向窗口绘图的“草稿”//随后建立与屏幕显示兼容的内存显示设备MemDC.CreateCompatibleDC(&dc); //这时还不能绘图,因为没有地方画 ^_^//创建的临时空白bitmap作为“画布”,至于位图的大小,可以用窗口的大小CBitmap MemBitmap;MemBitmap.CreateCompatibleBitmap(&dc,nWidth,nHeight);//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap); //将上面创建的临时“画布”MemBitmap与MemDC连接,注意此处的MemBitmap为一个空白临时画布,可以在这个空白画布上自绘图,也可以在这个画布上加载图片//先用背景色将位图清除干净,这里我用的是白色作为背景//你也可以用自己应该用的颜色MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));(2)在缓冲区画图MemDC.MoveTo(……);MemDC.LineTo(……);(2)'在第(2)步中,如果不是自绘图,而是加载一个位图,则需要再定义一个临时dc- MemDC2,用来将位图加载到上面建立的空白画布MemDC中CBitmap p1;//这是要画的位图,假设已加载好CDC MemDC2;MemDC2.CreateCompatibleDC(&dc);MemDC2.SelectObject(&p1);// MemDC2与图片链接//在这里,p1保存的是要加载到临时空白画布上的图片,MemDC2是与p1链接的dc(3)将缓冲区位图拷贝到当前画布(屏幕)上dc.BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);(3)’如果是位图的话首先,将与MemDC2链接的位图p1拷贝到临时空白画布MemDC中MemDC.BitBlt(x,y,width,height,& MemDC2,0,0,SRCCOPY); //向草稿绘制第一张图片,x,y,width,height请自行设置其次,将草稿绘制到屏幕上dc.BitBlt(0,0,width,height,&MemDC,0,0,SRCCOPY);(4)释放内存缓冲区//绘图完成后的清理MemBitmap.DeleteObject();MemDC.DeleteDC();MemDC2.DeleteDC();下面是一个不使用和使用双缓存的例子使用双缓存//CPoint ptCenter;//CRect rect, ellipseRect;//GetClientRect(&rect); //获得窗口客户区的大小//ptCenter = rect.CenterPoint(); //获得矩形的中心点,目的是为了确定后面同心圆图像的圆心//CDC dcMem; // 创建用于缓冲作图的内存DC对象dcMem//CBitmap bmp; // 创建内存中存放临时图像的位图对象bmp//dcMem.CreateCompatibleDC(pDC); // 依附窗口DC(窗口对象为pDC),创建兼容内存DC(就是创建一个内存DC,所有图形先画在这上面)//bmp.CreateCompatibleBitmap(&dcMem, rect.Width(), rect.Height());// 在兼容内存DC上,创建兼容位图//dcMem.SelectObject(&bmp); // 将位图选入内存DC//dcMem.FillSolidRect(rect, pDC->GetBkColor());// 按照原有背景色填充客户区,否则会成为黑色,同时也使内存DC的背景色保持一致//// 绘图操作//for (int i = 60; i > 0; --i)//{// ellipseRect.SetRect(ptCenter, ptCenter);// ellipseRect.InflateRect(i * 5, i * 5);// dcMem.Ellipse(ellipseRect); // 在内存DC上绘图,做同心圆图像//}//pDC->BitBlt(0, 0, rect.Width(), rect.Height(),// &dcMem, 0, 0, SRCCOPY); // 将内存DC上的图像复制到前台pDC,即实际屏幕对象pDC//dcMem.DeleteDC(); // 删除内存DC//bmp.DeleteObject(); // 删除内存位图不使用双缓存CPoint ptCenter;CRect rect,ellipseRect;GetClientRect(&rect);ptCenter = rect.CenterPoint();for(int i=60;i>0;i--){ellipseRect.SetRect(ptCenter,ptCenter);ellipseRect.InflateRect(i*5,i*5);pDC->Ellipse(ellipseRect);}下面的例子是加载两幅图片CBitmap p1,p2;//这是要画的位图,假设已加载好CDC dc;//这是窗口的DC,假设已加载好//创建两个临时dc,dc1为向窗口绘图的“草稿”,dc2为与源位图连接的dc(实际上dc2也可以用别的方法代替,这只是我的癖好)CDC dc1,dc2;dc1.CreateCompatibleDC(&DC);dc2.CreateCompatibleDC(&DC);//创建一个临时bitmap作为“画布”,与dc1连接CBitmap bm;CBitmap *Oldbm1,Oldbm2bm.CreateCompatibleBitmap(pDC,width,height); //长度宽度设置成与绘图面积一样大dc1.SelectObject(&bm);dc2.SelectObject(&p1);//dc2与第一张图片链接dc1.BitBlt(x,y, width,height,&dc2,0,0,SRCCOPY); //向草稿绘制第一张图片,x,y,width,height请自行设置dc2.SelectObject(&p2);//dc2与第一张图片链接dc1.BitBlt(x,y, width,height,&dc2,0,0,SRCCOPY); //向草稿绘制第二张图片//将草稿转移至窗口dc.BitBlt(0,0, width,height,&dc1,0,0,SRCCOPY);//清理工作...。

Java内存缓存工具GuavaLoadingCache使用解析

Java内存缓存工具GuavaLoadingCache使用解析

Java内存缓存⼯具GuavaLoadingCache使⽤解析这篇⽂章主要介绍了Java内存缓存⼯具Guava LoadingCache使⽤解析,⽂中通过⽰例代码介绍的⾮常详细,对⼤家的学习或者⼯作具有⼀定的参考学习价值,需要的朋友可以参考下⼀、Guava介绍Guava是Google guava中的⼀个内存缓存模块,⽤于将数据缓存到JVM内存中。

实际项⽬开发中经常将⼀些公共或者常⽤的数据缓存起来⽅便快速访问。

Guava Cache是单个应⽤运⾏时的本地缓存。

它不把数据存放到⽂件或外部服务器。

如果不符合需求,可以选择Memcached、Redis等⼯具。

⼆、代码⽰例1. POM引⼊<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>28.1-jre</version></dependency>2. 封装⼯具类package com.soyoung.ad.engine.util;import mon.cache.*;import lombok.extern.slf4j.Slf4j;import java.util.Map;import java.util.concurrent.TimeUnit;/*** 功能描述** @author 马振全 2020/1/13 16:18*/@Slf4jpublic class CacheManager {/** 缓存项最⼤数量 */private static final long GUAVA_CACHE_SIZE = 100000;/** 缓存时间:天 */private static final long GUAVA_CACHE_DAY = 10;/** 缓存操作对象 */private static LoadingCache<Long, String> GLOBAL_CACHE = null;static {try {GLOBAL_CACHE = loadCache(new CacheLoader<Long, String>() {@Overridepublic String load(Long key) throws Exception {// 处理缓存键不存在缓存值时的处理逻辑return "";}});} catch (Exception e) {log.error("初始化Guava Cache出错", e);}}/*** 全局缓存设置** 缓存项最⼤数量:100000* 缓存有效时间(天):10*** @param cacheLoader* @return*/private static LoadingCache<Long, String> loadCache(CacheLoader<Long, String> cacheLoader) throws Exception { LoadingCache<Long, String> cache = CacheBuilder.newBuilder()//缓存池⼤⼩,在缓存项接近该⼤⼩时, Guava开始回收旧的缓存项.maximumSize(GUAVA_CACHE_SIZE)//设置时间对象没有被读/写访问则对象从内存中删除(在另外的线程⾥⾯不定期维护).expireAfterAccess(GUAVA_CACHE_DAY, TimeUnit.DAYS)// 设置缓存在写⼊之后设定时间后失效.expireAfterWrite(GUAVA_CACHE_DAY, TimeUnit.DAYS)//移除监听器,缓存项被移除时会触发.removalListener(new RemovalListener<Long, String>() {@Overridepublic void onRemoval(RemovalNotification<Long, String> rn) {//逻辑操作}})//开启Guava Cache的统计功能.recordStats().build(cacheLoader);return cache;}/*** 设置缓存值* 注: 若已有该key值,则会先移除(会触发removalListener移除监听器),再添加** @param key* @param value*/public static void put(Long key, String value) {try {GLOBAL_CACHE.put(key, value);} catch (Exception e) {log.error("设置缓存值出错", e);}}/*** 批量设置缓存值** @param map*/public static void putAll(Map<? extends Long, ? extends String> map) {try {GLOBAL_CACHE.putAll(map);} catch (Exception e) {log.error("批量设置缓存值出错", e);}}/*** 获取缓存值* 注:如果键不存在值,将调⽤CacheLoader的load⽅法加载新值到该键中** @param key* @return*/public static String get(Long key) {String token = "";try {token = GLOBAL_CACHE.get(key);} catch (Exception e) {log.error("获取缓存值出错", e);}return token;}/*** 移除缓存** @param key*/public static void remove(Long key) {try {GLOBAL_CACHE.invalidate(key);log.error("移除缓存出错", e);}}/*** 批量移除缓存** @param keys*/public static void removeAll(Iterable<Long> keys) {try {GLOBAL_CACHE.invalidateAll(keys);} catch (Exception e) {log.error("批量移除缓存出错", e);}}/*** 清空所有缓存*/public static void removeAll() {try {GLOBAL_CACHE.invalidateAll();} catch (Exception e) {log.error("清空所有缓存出错", e);}}/*** 获取缓存项数量** @return*/public static long size() {long size = 0;try {size = GLOBAL_CACHE.size();} catch (Exception e) {log.error("获取缓存项数量出错", e);}return size;}}三、使⽤总结1. 移除机制guava做cache时候数据的移除分为被动移除和主动移除两种。

Java中的分布式缓存框架有哪些

Java中的分布式缓存框架有哪些

Java中的分布式缓存框架有哪些随着互联网应用的快速发展,分布式缓存已经成为了提高系统性能和扩展性的关键技术之一。

在Java开发领域,也涌现了许多优秀的分布式缓存框架。

本文将介绍几个Java中常用的分布式缓存框架,并分析它们的特点和适用场景。

一、EhcacheEhcache是一个开源的Java缓存框架,被广泛应用于各种Java应用中。

它提供了基于内存和磁盘的缓存机制,支持分布式部署,能够满足大规模应用的缓存需求。

Ehcache具有轻量级、易于使用和快速的特点,适合用于小型和中型的应用系统。

二、RedisRedis是一种高性能的内存数据存储系统,支持多种数据结构,可以用作分布式缓存的解决方案。

Redis提供了持久化和复制机制,可以实现高可用性和数据持久化。

同时,Redis还具有丰富的功能,如发布订阅、事务管理等,使得它不仅可以作为缓存系统,还可以用于其他用途,如消息队列等。

Redis适用于各种规模的应用系统。

三、MemcachedMemcached是一个简单的高性能分布式内存对象缓存系统。

它使用键值对的方式存储数据,提供了多种API,支持分布式部署。

Memcached具有高速的读写性能和可扩展性,通常被用于缓存数据库查询结果、页面内容等。

它适用于大规模应用和高并发场景,但需要注意的是,Memcached不提供数据持久化功能。

四、HazelcastHazelcast是一个基于Java的开源分布式缓存框架,它提供了分布式数据结构和集群管理功能。

Hazelcast采用了集中式架构,能够实现多节点之间的数据共享和同步。

它具有简单易用的特点,并提供了多种数据结构和并发算法的支持。

Hazelcast适用于构建复杂的分布式应用系统。

五、CaffeineCaffeine是一个在Java中最受欢迎的缓存库之一,它提供了高性能、无锁的内存缓存解决方案。

Caffeine采用了分片策略来管理缓存对象,提供了各种缓存策略和配置选项,可以根据实际需求进行灵活配置。

《Java性能调优指南》

《Java性能调优指南》

《Java性能调优指南》随着互联网的飞速发展,Java作为一种重要的编程语言,被越来越广泛地应用于各个领域。

但是,Java程序的性能问题也随之出现。

如何调优Java 程序的性能,成为了每个开发人员需要解决的难题。

本文将为大家介绍Java性能调优的指南。

一、JVM参数设置JVM(Java虚拟机)参数设置是Java性能调优的关键。

JVM有众多的参数,不同的参数设置会对Java程序的性能产生不同的影响。

常用的JVM参数设置包括以下几个方面:1. 内存设置内存是Java程序的一大瓶颈。

如果内存设置不合理,会导致Java程序频繁地进行垃圾回收,造成程序的延迟和不稳定。

在设置内存参数时需要注意以下几点:- -Xmx: 最大堆内存,设置合理的最大堆内存大小可以减少JVM的垃圾回收次数,提高程序性能。

- -Xms: 初始堆内存,设置合理的初始堆内存大小可以加快程序启动时间,提高程序性能。

- -XX:NewRatio: 新生代与老年代的比例,如果设置得当,可以减少垃圾回收的次数。

通常新生代的大小为总堆容量的1\/3或1\/4,老年代的大小为总堆容量的2\/3或3\/4。

2. 垃圾回收设置垃圾回收是Java程序中必不可少的一部分。

合理的垃圾回收参数设置可以提高程序性能。

常用的垃圾回收参数设置包括以下几点:- -XX:+UseParallelGC: 使用并行GC,适用于多核CPU。

- -XX:+UseConcMarkSweepGC: 使用CMS GC,适用于大型Web应用程序。

- -XX:+UseG1GC: 使用G1 GC,适用于大内存应用程序。

3. JIT设置JIT(即时编译器)是Java程序中非常重要的一部分。

合理的JIT参数设置可以提高程序的性能。

常用的JIT参数设置包括以下几点:- -XX:+TieredCompilation: 启用分层编译,可以提高程序启动时间和性能。

- -XX:CompileThreshold: JIT编译阈值,设置JIT编译的最小方法调用次数,可以提高程序性能。

java bytebuffer方法

java bytebuffer方法

一、介绍Java中的ByteBuffer是NIO中的一个重要类,它是一个字节缓冲区,在内存中可以存储字节数据。

ByteBuffer提供了丰富的方法,可以用于对字节数据进行读取、写入、复制和操作等操作。

本文将详细介绍Java中ByteBuffer的方法及其使用。

二、ByteBuffer的创建1. 调用allocate()方法创建ByteBuffer在Java中,我们可以通过调用allocate()方法来创建一个ByteBuffer 实例。

该方法的参数是缓冲区的容量,表示该缓冲区可以存储多少字节的数据。

例如:```ByteBuffer buffer = ByteBuffer.allocate(1024);```2. 使用wrap()方法创建ByteBuffer除了调用allocate()方法创建ByteBuffer外,我们还可以使用wrap()方法将一个已有的字节数组包装成一个ByteBuffer实例。

例如:```byte[] arr = new byte[1024];ByteBuffer buffer = ByteBuffer.wrap(arr);```三、ByteBuffer的常用方法1. put()方法put()方法用于向ByteBuffer中写入字节数据,可以写入单个字节、字节数组、或指定偏移量和长度的字节数组。

例如:```buffer.put((byte) 1); // 写入单个字节buffer.put(arr); // 写入整个字节数组buffer.put(arr, 0, 100); // 从arr数组的偏移量0开始写入100个字节```2. get()方法get()方法用于从ByteBuffer中读取字节数据,可以读取单个字节、字节数组,或指定偏移量和长度的字节数组。

例如:```byte b = buffer.get(); // 读取单个字节buffer.get(arr); // 读取整个字节数组buffer.get(arr, 0, 100); // 从ByteBuffer的当前位置读取100个字节到arr数组的偏移量0位置```3. flip()方法flip()方法用于切换读写模式,将缓冲区的position设置为0,limit设置为当前position,用于准备从缓冲区中读取数据。

服务器端利器

服务器端利器

服务器端利器服务器端利器--双缓冲队列应用服务器多线程IE算法JDK传统队列是生产者线程和消费者线程从同一个队列中存取数据,必然需要互斥访问,在互相同步等待中浪费了宝贵的时间,使队列吞吐量受影响。

双缓冲队使用两个队列,将读写分离,一个队列专门用来读,另一个专门用来写,当读队列空或写队列满时将两个队列互换。

这里为了保证队列的读写顺序,当读队列为空且写队列不为空时候才允许两个队列互换。

经过测试性能较JDK自带的queue的确有不小提高。

测试是和JDK6中性能最高的阻塞Queue:java.util.concurrent.ArrayBlockingQueue做比较,这个队列是环形队列的实现方式,性能还算不错,不过我们的目标是没有最好,只有更好。

测试场景:起若干个生产者线程,往Queue中放数据,起若干个消费者线程从queue中取数据,统计每个消费者线程取N个数据的平均时间。

数据如下:场景1生产者线程数:1消费者线程数:1Queue容量:5w取元素个数:1000wJDK ArrayBlockingQueue用时平均为:5,302,938,177纳秒双缓冲队列用时平均为:5,146,302,116纳秒相差大概160毫秒场景2:生产者线程数:5消费者线程数:4Queue容量:5w取元素个数:1000wJDK ArrayBlockingQueue用时平均为:32,824,744,868纳秒双缓冲队列用时平均为:20,508,495,221纳秒相差大概12.3秒可见在生产者消费者都只有一个的时候存和取的同步冲突比较小,双缓冲队列优势不是很大,当存取线程比较多的时候优势就很明显了。

队列主要方法如下:Java代码&lt;span style="font-size: small;"&gt;/** * * CircularDoubleBufferedQueue.java * 囧囧有神*@param &lt;E&gt;2010-6-12 */ public classCircularDoubleBufferedQueue&lt;E&gt; extends AbstractQueue&lt;E&gt; implementsBlockingQueue&lt;E&gt;, java.io.Serializable{ private static final long serialVersionUID = 1L; private Logger logger =Logger.getLogger(CircularDoubleBufferedQueue.class.get Name()); /** The queued items */ private final E[] itemsA; private final E[] itemsB;private ReentrantLock readLock, writeLock; private Condition notEmpty; private Condition notFull; private Condition awake; private E[] writeArray, readArray; private volatile int writeCount, readCount; private int writeArrayHP, writeArrayTP, readArrayHP, readArrayTP; public CircularDoubleBufferedQueue(int capacity){ if(capacity&lt;=0){ throw newIllegalArgumentException("Queue initial capacity can't less than 0!"); } itemsA =(E[])new Object[capacity]; itemsB = (E[])new Object[capacity]; readLock = new ReentrantLock(); writeLock = newReentrantLock(); notEmpty = readLock.newCondition(); notFull = writeLock.newCondition(); awake = writeLock.newCondition(); readArray = itemsA; writeArray = itemsB; }private void insert(E e){ writeArray[writeArrayTP] = e;++writeArrayTP; ++writeCount; }private E extract() { E e =readArray[readArrayHP];readArray[readArrayHP] = null; ++readArrayHP; --readCount; return e; } /** *switch condition: *read queue is empty&amp;&amp; write queue is not empty **Notice:This function can only be invoked after readLock is * grabbed,or may cause dead lock * @param timeout * @param isInfinite: whether need to wait forever until some other * thread awake it *@return * @throws InterruptedException */ private long queueSwitch(long timeout, boolean isInfinite) throws InterruptedException{ writeLock.lock(); try{ if (writeCount &lt;= 0){ logger.debug("Write Count:" + writeCount + ", Write Queue is empty, do not switch!"); try{ logger.debug("Queue is empty, need wait...."); if(isInfinite&amp;&amp; timeout&lt;=0){ awake.await();return -1; }else{ returnawake.awaitNanos(timeout); }} catch (InterruptedException ie){ awake.signal();throw ie; } }else { E[] tmpArray = readArray; readArray = writeArray; writeArray = tmpArray; readCount = writeCount; readArrayHP = 0; readArrayTP = writeArrayTP;writeCount = 0; writeArrayHP =readArrayHP; writeArrayTP = 0; notFull.signal(); logger.debug("Queue switch successfully!"); return-1; } } finally{ writeLock.unlock(); } } public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { if(e == null) { throw newNullPointerException(); }long nanoTime = unit.toNanos(timeout);writeLock.lockInterruptibly(); try{ for (;;){ if(writeCount &lt; writeArray.length) { insert(e);if (writeCount == 1){ awake.signal();} returntrue; }//Time out if(nanoTime&lt;=0){ logger.debug("offer wait time out!"); returnfalse; } //keepwaiting try{ logger.debug("Queue is full, need wait...."); nanoTime = notFull.awaitNanos(nanoTime); } catch(InterruptedException ie){ notFull.signal();throw ie; } } } finally{ writeLock.unlock(); } } public E poll(long timeout, TimeUnit unit) throws InterruptedException { long nanoTime = unit.toNanos(timeout);readLock.lockInterruptibly(); try{ for(;;){ if(readCount&gt;0){ returnextract(); }if(nanoTime&lt;=0){ logger.debug("poll time out!"); return null; }nanoTime = queueSwitch(nanoTime,false); } } finally{ readLock.unlock(); } }}&lt;/span&gt;附带队列类代码和测试类代码如下:欢迎大家提意见!Ps:测试时候要把queue类中debug关掉,否则打印debug 日志会对queue性能有不小的影响。

java 缓冲区的理解

java 缓冲区的理解

Java 缓冲区的理解在Java中,缓冲区(Buffer)是一种数据结构,用于在内存中临时存储数据。

它提供了一种有效的方式来处理输入和输出,以及在不同的组件之间传输数据。

Java 缓冲区广泛应用于文件操作、网络通信、数据库读写等场景中,它能够提高数据传输的效率,减少IO操作的次数。

1. 缓冲区的概念和作用缓冲区是一块连续的内存区域,它可以临时存储一定量的数据。

Java中提供了一系列缓冲区类(如ByteBuffer、CharBuffer、IntBuffer等),用于存储不同类型的数据。

缓冲区的作用主要有两个方面:•提高IO效率:通过读写缓冲区来减少IO操作的次数,从而提高程序的性能。

•实现数据交互:通过缓冲区在不同组件之间传输数据,如网络通信中的数据传输。

2. 缓冲区的基本原理缓冲区的基本原理是利用读写指针来跟踪数据的读写位置,并保持内存中的数据与外部数据源的同步。

在使用缓冲区进行数据读写时,需要先分配一个合适大小的缓冲区对象,然后通过读写指针来操作缓冲区中的数据。

读写指针包括位置(position)、限制(limit)和容量(capacity)三个属性:•位置(position):指示下一个读写操作的位置,默认初始值为0,每进行一次读写操作,位置会自动向后移动。

•限制(limit):指示读写的操作范围,它始终小于等于容量。

读操作时,限制等于缓冲区中的实际数据长度;写操作时,限制等于缓冲区容量。

•容量(capacity):缓冲区的总容量,它不会改变。

实际读写操作时,通过读写指针来操作缓冲区中的数据。

读操作会将位置指针向后移动,而写操作会将位置指针向前移动。

通过控制位置指针和限制,可以实现对缓冲区的灵活读写。

3. 缓冲区的类型Java提供了多种类型的缓冲区,以适应不同类型的数据读写需求。

常见的缓冲区类型有:•ByteBuffer:用于存储字节数据,是最常用的缓冲区类型。

可以通过allocate()方法来创建ByteBuffer对象,也可以通过wrap()方法来包装一个字节数组。

利用Java语言的双缓冲技术实现计算机动画

利用Java语言的双缓冲技术实现计算机动画

后 台视频缓存
( 一帧) 下
前 台视频 缓存 ( 前帧 ) 当
正地 显示到屏幕上 . 当画面成功地 绘制到缓冲 区
之后 , 利用 da i g 方法 将 当前 缓 冲 区 的 再 rwma e 夺椎前
i g 对象 绘制 到 ap t mae pl 区域 中, e 并且迅速 地覆 盖前 一个 画面。 有效地 减少 闪烁的情 况. 就是 也 说把原来影像清除, 显示影像 的复 杂工作 由看 不 见的 i ae m g 对象来 进行, 在 ap t 而 p l 上则只是 单 e 纯地 将画面慢慢循 序地 覆盖过去而 已. 现在我 们 相当严重 的闪烁情形 . 如流程 图 3 所示 . 变换后
样长的时间 . 视频 图像按此方式交替出现. 从一幅图像变 化到 下一幅 图像, 由于时 间板短 , 人眼是感觉 不 到这种变化的 . 采用双缓冲技术实现动画的过 程如 流程图 2 所示. 使用双缓冲时, 必须定义两个 i ae m g 对象, 一
个用来加载并显示影像 , 另一个则是 空白的影像 对象作为缓 冲, 并利用 ce mae r f g 方法建立对象. a i 此外还需要一 个 ga h a r { 类的对象, 来将下一 pc 用 个要显示的 画面绘制到 缓冲区中, 但是 并不是真

后再去调用 A pe 的 p i ( 方法完成重 画 pl t a t) n 动作. 当程序试图清除影像时, 在一个短暂 的
时间内. 整个 A p t p l 区域完全呈 现原来 的底 e 色, 而没有影像存在 , 虽然 时问非 常短暂. 但 是对于人 的眼睛 已经造成 闪烁的感 觉了. 很 显然, 除画面 的频率 越快 , 消 闪烁现 象越严
V 。. 0. . 1 2 No 1

java中repaint的用法

java中repaint的用法

java中repaint的用法在Java中,repaint()是一个非常重要的方法,它用于重新绘制组件。

当组件需要更新时,repaint()方法会被调用,以便重新绘制组件。

在本文中,我们将深入探讨repaint()方法的用法和实现。

repaint()方法的用法repaint()方法是在Component类中定义的。

它的语法如下:public void repaint()repaint()方法没有参数,它会重新绘制整个组件。

当调用repaint()方法时,Java会自动调用paint()方法来重新绘制组件。

paint()方法是在Component类中定义的,它用于绘制组件的内容。

repaint()方法可以在任何时候调用,但通常是在以下情况下调用:1. 当组件的大小或位置发生变化时,需要重新绘制组件。

2. 当组件的内容发生变化时,需要重新绘制组件。

3. 当组件需要更新时,需要重新绘制组件。

repaint()方法的实现repaint()方法的实现是非常复杂的。

它涉及到许多底层的绘图操作,包括图形缓冲、双缓冲、图形剪辑等。

在这里,我们只介绍repaint()方法的基本实现。

当调用repaint()方法时,Java会将该组件标记为需要重新绘制。

然后,Java会在下一个绘图周期中调用paint()方法来重新绘制组件。

在绘图周期中,Java会将组件的内容绘制到图形缓冲区中。

然后,Java会将图形缓冲区中的内容绘制到屏幕上。

为了提高绘图效率,Java使用了双缓冲技术。

双缓冲技术是指使用两个缓冲区来绘制图形。

当一个缓冲区被绘制完成后,Java会将其内容复制到另一个缓冲区中。

这样,当下一个绘图周期开始时,Java可以直接使用另一个缓冲区来绘制图形,从而提高绘图效率。

Java还使用了图形剪辑技术来限制绘图区域。

图形剪辑是指将绘图区域限制在一个矩形范围内。

这样,Java可以避免绘制不必要的图形,从而提高绘图效率。

总结repaint()方法是Java中一个非常重要的方法,它用于重新绘制组件。

双缓冲(DoubleBuffer)原理和使用

双缓冲(DoubleBuffer)原理和使用

双缓冲(DoubleBuffer)原理和使⽤⼀、双缓冲作⽤双缓冲甚⾄是多缓冲,在许多情况下都很有⽤。

⼀般需要使⽤双缓冲区的地⽅都是由于“⽣产者”和“消费者”供需不⼀致所造成的。

这样的情况在很多地⽅后可能会发⽣,使⽤多缓冲可以很好的解决。

我举⼏个常见的例⼦:例 1. 在⽹络传输过程中数据的接收,有时可能数据来的太快来不及接收导致数据丢失。

这是由于“发送者”和“接收者”速度不⼀致所致,在他们之间安排⼀个或多个缓冲区来存放来不及接收的数据,让速度较慢的“接收者”可以慢慢地取完数据不⾄于丢失。

例2. 再如,计算机中的三级缓存结构:外存(硬盘)、内存、⾼速缓存(介于CPU和内存之间,可能由多级)。

从左到右他们的存储容量不断减⼩,但速度不断提升,当然价格也是越来越贵。

作为“⽣产者”的 CPU 处理速度很快,⽽内存存取速度相对CPU较慢,如果直接在内存中存取数据,他们的速度不⼀致会导致 CPU 能⼒下降。

因此在他们之间⼜增加的⾼速缓存来作为缓冲区平衡⼆者速度上的差异。

例3. 在图形图像显⽰过程中,计算机从显⽰缓冲区取数据然后显⽰,很多图形的操作都很复杂需要⼤量的计算,很难访问⼀次显⽰缓冲区就能写⼊待显⽰的完整图形数据,通常需要多次访问显⽰缓冲区,每次访问时写⼊最新计算的图形数据。

⽽这样造成的后果是⼀个需要复杂计算的图形,你看到的效果可能是⼀部分⼀部分地显⽰出来的,造成很⼤的闪烁不连贯。

⽽使⽤双缓冲,可以使你先将计算的中间结果存放在另⼀个缓冲区中,但全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据⼀次性复制到显⽰缓冲区。

例1 中使⽤双缓冲是为了防⽌数据丢失,例2 中使⽤双缓冲是为了提⾼ CPU 的处理效率,⽽例3使⽤双缓冲是为了防⽌显⽰图形时的闪烁延迟等不良体验。

⼆、双缓冲原理这⾥,主要以双缓冲在图形图像显⽰中的应⽤做说明。

上⾯例3中提到了双缓冲的主要原理,这⾥通过⼀个图再次理解⼀下:图 1 双缓冲⽰意图注意,显⽰缓冲区是和显⽰器⼀起的,显⽰器只负责从显⽰缓冲区取数据显⽰。

jvm codecache 参数

jvm codecache 参数

jvm codecache 参数JVM(Java Virtual Machine)是一种可以运行Java字节码的虚拟机。

而JVM的CodeCache(代码缓存)是JVM中的一个重要组件,用于存储已经被即时编译器(Just-in-Time Compiler)编译为本地机器码的Java字节码。

本文将以"JVM CodeCache 参数"为主题,详细介绍JVM CodeCache的作用、配置、优化以及可能出现的问题。

第一部分:JVM CodeCache 简介JVM CodeCache是JVM用来缓存已经编译为本地机器码的Java字节码的内存区域。

它的主要作用是提高Java程序的运行效率,通过缓存频繁执行的代码,避免反复的进行即时编译。

CodeCache 通常位于Java堆外,有着较小的初始大小。

CodeCache分为两部分:方法区域的CodeHeap和HotSpot VM自身用于编译器对象的内存区域CompactibleFreeListSpace。

前者主要用于缓存已经编译的方法,后者则用于缓存编译器生成的其他对象。

第二部分:JVM CodeCache 的配置JVM CodeCache的大小可以通过JVM启动参数进行配置。

以下是一些常用的CodeCache参数:1. `-XX:InitialCodeCacheSize`:指定初始的CodeCache大小,默认大小为240MB。

2. `-XX:ReservedCodeCacheSize`:指定CodeCache的最大大小,当CodeCache超过该大小时,JVM会尝试进行垃圾回收以释放空间。

3. `-XX:CodeCacheExpansionSize`:当CodeCache空间不足时,JVM会试图扩展其大小,该参数用于指定扩展的大小。

4. `-XX:CodeCacheMinimumFreeSpace`:指定CodeCache 中用于碎片整理(Compaction)的最小剩余空间。

详解javaIO流之缓冲流的使用

详解javaIO流之缓冲流的使用

详解javaIO流之缓冲流的使⽤java缓冲流本⾝不具IO功能,只是在别的流上加上缓冲提⾼效率,像是为别的流装上⼀种包装。

当对⽂件或其他⽬标频繁读写或操作效率低,效能差。

这时使⽤缓冲流能够更⾼效的读写信息。

因为缓冲流先将数据缓存起来,然后⼀起写⼊或读取出来。

所以说,缓冲流还是很重要的,在IO操作时记得加上缓冲流提升性能。

缓冲流分为字节和字符缓冲流字节缓冲流为:BufferedInputStream—字节输⼊缓冲流BufferedOutputStream—字节输出缓冲流字符缓冲流为:BufferedReader—字符输⼊缓冲流BufferedWriter—字符输出缓冲流下⾯主要介绍这四种缓冲流的使⽤。

⼀. 字节缓冲流1.BufferedOutputStream—字节输出缓冲流BufferedOutputStream类实现缓冲的输出了,通过设置这种输出流,应⽤程序就可以将各个字节写⼊底层输出流中,⽽不必每⼀个字节写⼊都调⽤底层系统。

⽰例代码:public static void main(String[] args) {try {//创建字节输出流实例OutputStream out=new FileOutputStream("L:\\test.txt");//根据字节输出流构建字节缓冲流BufferedOutputStream buf=new BufferedOutputStream(out);String data="好好学习,天天向上";buf.write(data.getBytes());//写⼊缓冲区buf.flush();//刷新缓冲区,即把内容写⼊//关闭流buf.close();//关闭缓冲流时,也会刷新⼀次缓冲区out.close();} catch (IOException e) {e.printStackTrace();}}2.BufferedInputStream—字节输⼊缓冲流BufferedInputStream为别的输⼊流添加缓冲功能,在创建BufferedInputStream时会创建⼀个内部缓冲数组,⽤于缓冲数据,提⾼性能。

java_lang_memorypool 指标说明

java_lang_memorypool 指标说明

java_lang_memorypool 指标说明Java Lang MemoryPool 指标说明1. 简介Java Lang MemoryPool 是 Java 虚拟机(JVM)中负责管理内存的一个重要组件。

它负责分配、回收和管理 Java 程序所需的内存空间,以供对象的创建和存储使用。

2. 常见的 MemoryPool 类型以下是常见的 MemoryPool 类型:•Eden Space:新对象的初始分配和大部分对象的生命周期都在Eden Space 中进行。

•Survivor Space:当 Eden Space 中的对象经过一次 Minor GC 后仍然存活,就会被复制到 Survivor Space。

•Tenured(Old) Space:经过多次 Minor GC 后仍然存活的对象会被晋升到 Tenured Space。

•Perm(Metaspace):用于存储类的元数据信息。

3. MemoryPool 指标详解以下是常见的 MemoryPool 指标及其说明:•Current Usage:当前已使用的内存量,单位为字节。

•Peak Usage:内存使用峰值,即在指定时间段内的最大内存使用量。

•Collection Usage:垃圾回收期间使用的内存量。

•Collection Usage Threshold:内存池使用达到某个阈值时触发垃圾回收的阈值设置。

4. 使用 MemoryPool 指标进行性能调优利用 MemoryPool 指标可以帮助我们进行 Java 程序的性能调优。

以下是一些优化建议:•监控 Current Usage 和 Peak Usage,如果某个内存池的使用量持续增长,可能存在内存泄漏,需要进一步排查。

•根据 Collection Usage 和 Collection Usage Threshold 调整垃圾回收策略,以减少性能损耗。

•对于 Perm(Metaspace),可以通过调整 MaxPermSize(-XX:MaxPermSize)或 MaxMetaspaceSize(-XX:MaxMetaspaceSize)参数来控制其大小。

java 中io流的实现机制

java 中io流的实现机制

java 中io流的实现机制Java中的IO流实现机制主要依赖于字节缓冲区(ByteBuffer)和通道(Channel)。

1. 字节缓冲区(ByteBuffer):字节缓冲区是一个用于存储字节数据的容器,它提供了一种高效的方式来读写数据。

在Java NIO中,字节缓冲区分为直接缓冲区(Direct Buffer)和非直接缓冲区(Non-Direct Buffer)。

直接缓冲区是由操作系统分配内存的,而非直接缓冲区则是由Java堆内存分配的。

字节缓冲区的主要操作包括读取、写入、翻转等。

2. 通道(Channel):通道是Java NIO中的一个抽象概念,它可以表示文件、网络连接等I/O资源。

通道提供了一种高效的数据传输方式,通过通道可以将数据从一个地方传输到另一个地方。

通道的主要操作包括打开、关闭、注册回调函数等。

Java中的IO流实现机制主要包括以下几个步骤:1. 创建一个字节缓冲区,用于存储要读写的数据。

2. 创建一个通道,用于表示要操作的I/O资源。

3. 将字节缓冲区与通道关联起来,以便进行数据传输。

4. 使用通道的read()或write()方法进行数据的读写操作。

5. 当数据传输完成后,关闭通道和字节缓冲区。

以下是一个简单的Java NIO文件读取示例:```javaimport java.io.FileInputStream;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;public class NIOFileReadExample {public static void main(String[] args) {FileInputStream fis = null;FileChannel fileChannel = null;ByteBuffer buffer = ByteBuffer.allocate(1024);try {fis = new FileInputStream("example.txt");fileChannel = fis.getChannel();while (fileChannel.read(buffer) != -1) {buffer.flip(); // 切换为读模式while (buffer.hasRemaining()) {System.out.print((char) buffer.get());}buffer.clear(); // 清空缓冲区,准备下一次写入}} catch (IOException e) {e.printStackTrace();} finally {try {if (fileChannel != null) {fileChannel.close();}if (fis != null) {fis.close();}} catch (IOException e) {e.printStackTrace();}}}}```这个示例中,我们使用了Java NIO的文件通道(FileChannel)和字节缓冲区(ByteBuffer)来实现文件的读取操作。

java图像显示双缓冲技术

java图像显示双缓冲技术

在Java编写具有连贯变化的窗口程序时,通常的办法是在子类中覆盖父类的paint(Graphics)方法,在方法中使用GUI函数实现窗口重绘的过程。

连贯变换的窗口会不断地调用update(Graphics)函数,该函数自动的调用paint(Graphics)在Java编写具有连贯变化的窗口程序时,通常的办法是在子类中覆盖父类的paint(Graphics)方法,在方法中使用GUI函数实现窗口重绘的过程。

连贯变换的窗口会不断地调用update(Graphics)函数,该函数自动的调用paint(Graphics)函数。

这样就会出现闪烁的情况。

为了解决这一问题,可以应用双缓冲技术。

可以通过截取上述过程,覆盖update(Graphics)函数,在内存中创建一个与窗口大小相同的图形,并获得该图形的图形上下文(Graphics),再将图片的图形上下文作为参数调用paint(Graphics)函数(paint(Graphics)中的GUI函数会在图片上画图),再在update(Graphics)函数调用drawImage函数将创建的图形直接画在窗口上。

01.Image ImageBuffer = null;02.Graphics GraImage = null;03.04.public void update(Graphics g){ //覆盖update方法,截取默认的调用过程05. ImageBuffer = createImage(this.getWidth(), this.getHeight());//创建图形缓冲区06. GraImage = ImageBuffer.getGraphics(); //获取图形缓冲区的图形上下文07. paint(GraImage); //用paint方法中编写的绘图过程对图形缓冲区绘图08. GraImage.dispose(); //释放图形上下文资源09. g.drawImage(ImageBuffer, 0, 0, this); //将图形缓冲区绘制到屏幕上10.}11.12.public void paint(Graphics g){ //在paint方法中实现绘图过程13. g.drawLine(0, 0, 100, 100);14.}Image ImageBuffer = null;Graphics GraImage = null;public void update(Graphics g){ //覆盖update方法,截取默认的调用过程ImageBuffer = createImage(this.getWidth(), this.getHeight()); //创建图形缓冲区GraImage = ImageBuffer.getGraphics(); //获取图形缓冲区的图形上下文paint(GraImage); //用paint方法中编写的绘图过程对图形缓冲区绘图GraImage.dispose(); //释放图形上下文资源g.drawImage(ImageBuffer, 0, 0, this); //将图形缓冲区绘制到屏幕上}public void paint(Graphics g){ //在paint方法中实现绘图过程g.drawLine(0, 0, 100, 100);}因为大部分绘图过程是在内存中进行,所以有效地消除了闪烁。

tlab在java中的作用

tlab在java中的作用

在Java语言中,threadlocalallocationbuffer(tlab)是一种特殊的内存分配方式,它可以大大加速Java对象实例的内存分配速度。

通常来说,Java程序在运行过程中会产生大量的对象实例,这些实例需要在Java堆上分配内存。

在传统的内存分配方式中,每个线程在分配内存时都需要从Java堆中获取内存空间,这种方式导致了大量的内存争用和同步操作。

tlab的出现,则有效地解决了这个问题。

它的基本思想是在每个线程中预分配一小块私有内存,称为本地缓存(TLAB)。

只有当本地缓存用完,需要分配新的内存空间时,才需要进行同步锁定。

这样一来,每个线程在执行任务时,都可以独享一块内存,避免了大量的内存争用和同步操作,从而大大提高了程序运行的效率。

tlab的空间内存非常小,缺省情况下仅占有整个Eden空间的1%。

这意味着,在绝大多数情况下,Java程序可以使用tlab进行内存分配,而无需担心内存资源的过度消耗。

然而,当遇到大对象分配时,可能会出现TLAB空间无法容纳的情况。

此时,程序就需要使用其他的内存分配策略,例如增加本地缓存的大小、使用其他内存分配方式等。

使用TLAB时,有几点需要注意:1. 避免频繁使用:TLAB是一种操作系统级别的内存管理机制,频繁地使用TLAB可能会导致频繁的内存分配和释放操作,这可能会影响系统的性能。

2. 控制TLAB的大小:TLAB的大小可以通过使用setThreadLocalAreaMaxSize()函数来控制。

对于较小的TLAB,可能会导致内存碎片和内存溢出,而对于较大的TLAB,可能会导致TLAB 内存泄露。

因此,需要根据实际情况选择合适的TLAB大小。

3. 避免TLAB冲突:在多线程环境中,使用TLAB时可能会出现TLAB冲突的问题。

当两个或更多的线程同时使用同一个TLAB时,可能会导致数据错误或内存泄漏。

因此,需要在多线程环境中进行同步或锁定操作来避免TLAB冲突。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

1.Java 双缓冲技术Java 的强大特性让其在游戏编程和多媒体动画处理方面也毫不逊色。

Java 游戏编程和动画编程中最常见在的就是对于屏幕闪烁的处理。

本文从 J2SE 的一个再现了屏幕闪烁的 JavaAppilication 简单动画实例展开,对屏幕闪烁的原因进行了分析,找出了闪烁成因的关键:update(Graphics g)函数对于前端屏幕的清屏。

由此引出消除闪烁的方法——双缓冲。

双缓冲是计算机动画处理中的传统技术,在用其他语言编程时也可以实现。

本文从实例出发,着重介绍了用双缓冲消除闪烁的原理以及双缓冲在 Java 中的两种常用实现方法(即在 update(Graphics g)中实现和在paint(Graphics g)中实现),以期读者能对双缓冲在 Java 编程中的应用能有个较全面的认识。

一、问题的引入在编写 Java 多媒体动画程序或用 Java 编写游戏程序的时候,我们得到的动画往往存在严重的闪烁(或图片断裂)。

这种闪烁虽然不会给程序的效果造成太大的影响,但着实有违我们的设计初衷,也给程序的使用者造成了些许不便。

闪烁到底是什么样的呢?下面的 JavaApplication 再现了这种屏幕闪烁的情况://代码段一[①],闪烁的再现import java.awt.*;import java.awt.event.*;public class DoubleBuffer extends Frame//主类继承 Frame 类{public paintThreadpT;//绘图线程public intypos=-80; //小圆左上角的纵坐标public DoubleBuffer()//构造函数{pT=new paintThread(this);this.setResizable(false);this.setSize(300,300); //设置窗口的首选大小this.setVisible(true); //显示窗口pT.start();//绘图线程启动}public void paint(Graphics scr) //重载绘图函数{scr.setColor(Color.RED);//设置小圆颜色scr.fillOval(90,ypos,80,80); //绘制小圆}public static void main(String[] args){DoubleBuffer DB=new DoubleBuffer();//创建主类的对象DB.addWindowListener(new WindowAdapter()//添加窗口关闭处理函数{public void windowClosing(WindowEvent e){System.exit(0);}});}}class paintThread extends Thread//绘图线程类{DoubleBuffer DB;public paintThread(DoubleBuffer DB) //构造函数{this.DB=DB;}public void run()//重载 run()函数{while(true)//线程中的无限循环{try{sleep(30); //线程休眠 30ms}catch(InterruptedException e){}DB.ypos+=5; //修改小圆左上角的纵坐标if(DB.ypos>300) //小圆离开窗口后重设左上角的纵坐标DB.ypos=-80;DB.repaint();//窗口重绘}}}编译、运行上述例子程序后,我们会看到窗体中有一个从上至下匀速运动的小圆,但仔细观察,你会发现小圆会不时地被白色的不规则横纹隔开,即所谓的屏幕闪烁,这不是我们预期的结果。

这种闪烁是如何出现的呢?首先我们分析一下这段代码。

DoubleBuffer 的对象建立后,显示窗口,程序首先自动调用重载后的 paint(Graphics g)函数,在窗口上绘制了一个小圆,绘图线程启动后,该线程每隔 30ms 修改一下小圆的位置,然后调用 repaint()函数。

注意,这个 repaint()函数并不是我们重载的,而是从 Frame 类继承而来的。

它先调用 update(Graphics g)函数,update(Graphics g)再调用 paint(Graphics g)函数[②]。

问题就出在update(Graphics g)函数,我们来看看这个函数的源代码:public void update(Graphics g){if (isShowing()){if (! (peer instanceofLightweightPeer)){g.clearRect(0, 0, width, height);}paint(g);}}以上代码的意思是:(如果该组件是轻量组件的话)先用背景色覆盖整个组件,然后再调用 paint(Graphics g)函数,重新绘制小圆。

这样,我们每次看到的都是一个在新的位置绘制的小圆,前面的小圆都被背景色覆盖掉了。

这就像一帧一帧的画面匀速地切换,以此来实现动画的效果。

但是,正是这种先用背景色覆盖组件再重绘图像的方式导致了闪烁。

在两次看到不同位置小圆的中间时刻,总是存在一个在短时间内被绘制出来的空白画面(颜色取背景色)。

但即使时间很短,如果重绘的面积较大的话花去的时间也是比较可观的,这个时间甚至可以大到足以让闪烁严重到让人无法忍受的地步。

另外,用 paint(Graphics g)函数在屏幕上直接绘图的时候,由于执行的语句比较多,程序不断地改变窗体中正在被绘制的图象,会造成绘制的缓慢,这也从一定程度上加剧了闪烁。

就像以前课堂上老师用的旧式的幻灯机,放完一张胶片,老师会将它拿下去,这个时候屏幕上一片空白,直到放上第二张,中间时间间隔较长。

当然,这不是在放动画,但上述闪烁的产生原因和这很类似。

二、问题的解决知道了闪烁产生的原因,我们就有了更具针对性的解决闪烁的方案。

已经知道 update(Graphics g)是造成闪烁的主要原因,那么就从这里入手。

(1)尝试这样重载 update(Graphics g)函数(基于代码段一修改):public void update(Graphics scr){paint(scr);}以上代码在重绘小圆之前没有用背景色重绘整个画面,而是直接调用 paint(Graphics g)函数,这就从根本上避免了上述的那幅空白画面。

看看运行结果,闪烁果然消除了!但是更大的问题出现了,不同时刻绘制的小圆重叠在一起形成了一条线!这样的结果我们更不能接受了。

为什么会这样呢?仔细分析一下,重载后的 update(Graphics g)函数中没有了任何清屏的操作,每次重绘都是在先前已经绘制好的图象的基础上,当然会出现重叠的现象了。

2)使用双缓冲:这是本文讨论的重点。

所谓双缓冲,就是在内存中开辟一片区域,作为后台图象,程序对它进行更新、修改,绘制完成后再显示到屏幕上。

1、重载 paint(Graphics g)实现双缓冲:这种方法要求我们将双缓冲的处理放在 paint(Graphics g)函数中,那么具体该怎么实现呢?先看下面的代码(基于代码段一修改):在 DoubleBuffer 类中添加如下两个私有成员:private Image iBuffer;private Graphics gBuffer;重载 paint(Graphics scr)函数:public void paint(Graphics scr){if(iBuffer==null){iBuffer=createImage(this.getSize().width,this.getSize().he ight);gBuffer=iBuffer.getGraphics();}gBuffer.setColor(getBackground());gBuffer.fillRect(0,0,this.getSize().width,this.getSize().h eight);gBuffer.setColor(Color.RED);gBuffer.fillOval(90,ypos,80,80);scr.drawImage(iBuffer,0,0,this);}分析上述代码:我们首先添加了两个成员变量 iBuffer 和 gBuffer 作为缓冲(这就是所谓的双缓冲名字的来历)。

在 paint(Graphics scr)函数中,首先检测如果 iBuffer 为 null,则创建一个和屏幕上的绘图区域大小一样的缓冲图象,再取得 iBuffer 的 Graphics 类型的对象的引用,并将其赋值给 gBuffer,然后对 gBuffer 这个内存中的后台图象先用 fillRect(int,int,int,int)清屏,再进行绘制操作,完成后将 iBuffer 直接绘制到屏幕上。

这段代码看似可以完美地完成双缓冲,但是,运行之后我们看到的还是严重的闪烁!为什么呢?回想上文所讨论的,问题还是出现在update(Graphics g)函数!这段修改后的程序中的update(Graphics g)函数还是我们从父类继承的。

在update(Graphics g)中,clearRect(int,int,int,int)对前端屏幕进行了清屏操作,而在 paint(Graphics g) 中,对后台图象又进行了清屏操作。

那么如果保留后台清屏,去掉多余的前台清屏应该就会消除闪烁。

所以,我们只要按照(1)中的方法重载update(Graphics g)即可:public void update(Graphics scr){paint(scr);}这样就避开了对前端图象的清屏操作,避免了屏幕的闪烁。

虽然和(1)中用一样的方法重载 update(Graphics g),但(1)中没有了清屏操作,消除闪烁的同时严重破坏了动画效果,这里我们把清屏操作放在了后台图象上,消除了闪烁的同时也获得了预期的动画效果。

2、重载 update(Graphics g)实现双缓冲:这是比较传统的做法。

也是实际开发中比较常用的做法。

我们看看实现这种方法的代码(基于代码段一修改):在 DoubleBuffer 类中添加如下两个私有成员:private Image iBuffer;private Graphics gBuffer;重载 paint(Graphics scr)函数:public void paint(Graphics scr){scr.setColor(Color.RED);scr.fillOval(90,ypos,80,80);}重载 update(Graphics scr)函数:public void update(Graphics scr){if(iBuffer==null){iBuffer=createImage(this.getSize().width,this.getSize().height);gBuffer=iBuffer.getGraphics();}gBuffer.setColor(getBackground());gBuffer.fillRect(0,0,this.getSize().width,this.getSize().h eight);paint(gBuffer);scr.drawImage(iBuffer,0,0,this);}分析上述代码:我们把对后台图象的创建、清屏以及重绘等一系列动作都放在了 update(Graphics scr)函数中,而 paint(Graphics g)函数只是负责绘制什么样的图象,以及怎样绘图,函数的最后实现了后台图象向前台绘制的过程。

相关文档
最新文档