技术标签: 缓存
public static void initCache(LoadingCache cache) throws ExecutionException {
for(int i =1;i<=3;i++){
//连接数据源 ,如果缓存没有就读取数据源
cache.get(String.valueOf(i));
}
}
/**
* 获得当前缓存的记录
* @param cache
* @throws Exception
*/
public static void displayCache(LoadingCache cache)throws Exception{
Iterator its=cache.asMap().entrySet().iterator();
while(its.hasNext()){
System.out.println(its.next().toString());
}
}
public static void main(String[] args) throws Exception {
//CacheLoader方式
LoadingCache<String,Object> cache=CacheBuilder.newBuilder()
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
return Constants.hm.get(key);
}
});
//初始化
initCache(cache);
LoadingCache<String,Object> cache=CacheBuilder.newBuilder()
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
return Constants.hm.get(key);
}
});
//CacheLoader方式
LoadingCache<String,Object> cache=CacheBuilder.newBuilder()
//最大个数3
.maximumSize(3)
//统计
.recordStats()
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(" onRemoval key "+notification.getKey()+" 原因: "+notification.getCause());
}
})
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
return Constants.hm.get(key);
}
});
//初始化
initCache(cache);
System.out.println(cache.size());
Object o1 = cache.getIfPresent("1");
System.out.println("========================"+o1);
displayCache(cache);
Object o = get("4", cache);
System.out.println("========================");
displayCache(cache);
System.out.println(cache.stats());
//CacheLoader方式
LoadingCache<String,Object> cache=CacheBuilder.newBuilder()
//最大个数3
.maximumSize(3)
.expireAfterAccess(3, TimeUnit.SECONDS)
//统计
.recordStats()
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(" onRemoval key "+notification.getKey()+" 原因: "+notification.getCause());
}
})
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
return Constants.hm.get(key);
}
});
//初始化
initCache(cache);
//显示缓存数据
displayCache(cache);
Thread.sleep(1000);
//访问1
cache.getIfPresent("1");
//歇了2.1秒
Thread.sleep(2100);
System.out.println("==================================");
displayCache(cache);
//CacheLoader方式
LoadingCache<String,Object> cache=CacheBuilder.newBuilder()
//最大个数3
.maximumSize(3)
.expireAfterWrite(3, TimeUnit.SECONDS)
//统计
.recordStats()
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(" onRemoval key "+notification.getKey()+" 原因: "+notification.getCause());
}
})
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
return Constants.hm.get(key);
}
});
//初始化
initCache(cache);
//显示缓存数据
displayCache(cache);
Thread.sleep(1000);
//访问1
cache.getIfPresent("1");
//歇了2.1秒
Thread.sleep(2100);
System.out.println("==================================");
displayCache(cache);
//CacheLoader方式
LoadingCache<String,Object> cache=CacheBuilder.newBuilder()
//最大个数3
.maximumSize(3)
.weakValues()
//统计
.recordStats()
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(" onRemoval key "+notification.getKey()+" 原因: "+notification.getCause());
}
})
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
return Constants.hm.get(key);
}
});
//初始化
initCache(cache);
//显示缓存数据
displayCache(cache);
Object v = new Object();
cache.put("1",v);
v = new Object();
System.gc();
System.out.println("=============");;
displayCache(cache);
//CacheLoader方式
LoadingCache<String,Object> cache=CacheBuilder.newBuilder()
//最大个数3
.maximumSize(3)
//统计
.recordStats()
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(" onRemoval key "+notification.getKey()+" 原因: "+notification.getCause());
}
})
.build(new CacheLoader<String, Object>() {
@Override
public Object load(String key) throws Exception {
return Constants.hm.get(key);
}
});
//初始化
initCache(cache);
//显示缓存数据
displayCache(cache);
//删除所有
//cache.invalidateAll();
//删除指定
// cache.invalidate("1");
cache.invalidateAll(Arrays.asList("1","2"));
System.out.println("=============");
displayCache(cache);
Guava Cache的数据结构跟ConcurrentHashMap类似,但也不完全一样。最基本的区别是
ConcurrentMap会一直保存所有添加的元素,直到显式地移除。
相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。其数据结构图如下
AtomicReferenceArray<ReferenceEntry<K, V>> table:AtomicReferenceArray可以用原子方式
更新其元素的对象引用数组
ReferenceEntry是Guava Cache中对一个键值对节点的抽象,每个ReferenceEntry数组项都是一条ReferenceEntry链。并且一个ReferenceEntry包含key、hash、valueReference、next字段(单链)
Guava Cache使用ReferenceEntry接口来封装一个键值对,而用ValueReference来封装Value值
除了以上三种还有主动删除,采用命令,
GuavaCache构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。
GuavaCache是在每次进行缓存操作的时候,惰性删除 如get()或者put()的时候,判断缓存是否过期
通过key做hash定位到所在的Segment
通过位运算找首地址的偏移量 SegmentCount>=并发数且为2的n次方
V get(K key, CacheLoader<? super K, V> loader) throws ExecutionException {
// 注意,key不可为空
int hash = hash(checkNotNull(key));
// 通过hash定位到segment数组的某个Segment元素,然后调用其get方法
return segmentFor(hash).get(key, hash, loader);
}
再找到segment中的Entry链数组,通过key的hash定位到某个Entry节点
V get(K key, int hash, CacheLoader<? super K, V> loader) throws
ExecutionException {
checkNotNull(key);
checkNotNull(loader);
try {
if (count != 0) {
// read-volatile
// 内部也是通过找Entry链数组定位到某个Entry节点
ReferenceEntry<K, V> e = getEntry(key, hash);
......
GuavaCache通过设置 concurrencyLevel 使得缓存支持并发的写入和读取
LoadingCache<String,Object> cache = CacheBuilder.newBuilder()
// 最大3个 同时支持CPU核数线程写缓存
.maximumSize(3)
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
.build();
concurrencyLevel=Segment数组的长度
默认长度4
同ConcurrentHashMap类似Guava cache的并发也是通过分离锁实现
V get(K key, CacheLoader<? super K, V> loader) throws ExecutionException {
int hash = this.hash(Preconditions.checkNotNull(key));
//通过hash值确定该key位于哪一个segment上,并获取该segment
return this.segmentFor(hash).get(key, hash, loader);
}
LoadingCache采用了类似ConcurrentHashMap的方式,将映射表分为多个segment。segment之间可以并发访问,这样可以大大提高并发的效率,使得并发冲突的可能性降低了。
GuavaCache提供了一个refreshAfterWrite定时刷新数据的配置项
如果经过一定时间没有更新或覆盖,则会在下一次获取该值的时候,会在后台异步去刷新缓存刷新时只有一个请求回源取数据,其他请求会阻塞(block)在一个固定时间段,如果在该时间段内没有获得新值则返回旧值。
LoadingCache<String,Object> cache = CacheBuilder.newBuilder()
// 最大3个 同时支持CPU核数线程写缓存
.maximumSize(3).concurrencyLevel(Runtime.getRuntime().availableProcessors()).
//3秒内阻塞会返回旧数据
refreshAfterWrite(3,TimeUnit.SECONDS).build();
动态加载行为发生在获取不到数据或者是数据已经过期的时间点,Guava动态加载使用回调模式
用户自定义加载方式,然后Guava cache在需要加载新数据时会回调用户的自定义加载方式
segmentFor(hash).get(key, hash, loader)
loader即为用户自定义的数据加载方式,当某一线程get不到数据会去回调该自定义加载方式去加载数
据
public class LRUcache<k, v> extends LinkedHashMap<k, v> {
private final int limit;
public LRUcache(int limit) {
//初始化 accessOrder : true 改变尾结点
super(16, 0.75f, true);
this.limit = limit;
}
//是否删除最老的数据
@Override
protected boolean removeEldestEntry(Map.Entry<k, v> eldest) {
return size() > limit;
}
}
//
public class LinkedHashLRUcache<k,v> {
/**
* LinkedHashMap(自身实现了LRU算法)
* 有序
* 每次访问一个元素,都会加到尾部
*/
int limit;
LRUcache<k, v> lruCache;
public LinkedHashLRUcache(int limit) {
this.limit = limit;
this.lruCache = new LRUcache(limit);
}
public void put(k key, v value) {
this.lruCache.put(key, value);
}
public v get(k key) {
return this.lruCache.get(key);
}
public static void main(String[] args) {
LinkedHashLRUcache lru = new LinkedHashLRUcache(3);
lru.put(1, "zhangfei1");
lru.put(2, "zhangfei2");
lru.put(3, "zhangfei3");
lru.get(1);
lru.put(4, "zhangfei4");
for (Object o : lru.lruCache.values()) {
System.out.println(o.toString());
}
}
}
会,当我们设置缓存永不过期(或者很长),缓存的对象不限个数(或者很大)时,比如:
Cache<String, String> cache = CacheBuilder.newBuilder()
.expireAfterWrite(100000, TimeUnit.SECONDS)
.build();
不断向GuavaCache加入大字符串,最终将会oom
解决方案:缓存时间设置相对小些,使用弱引用方式存储对象
Cache<String, String> cache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.weakValues().build();
不是的,GuavaCache是在每次进行缓存操作的时候,如get()或者put()的时候,判断缓存是否过期
void evictEntries(ReferenceEntry<K, V> e) {
drainRecencyQueue();
while ((e = writeQueue.peek()) != null && map.isExpired(e, now)) {
if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
throw new AssertionError();
}
} w
hile ((e = accessQueue.peek()) != null && map.isExpired(e, now)) {
if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
throw new AssertionError();
}
}
}
一个如果一个对象放入缓存以后,不在有任何缓存操作(包括对缓存其他key的操作),那么该缓存不会主动过期的
用accessQueue,这个队列是按照LRU的顺序存放的缓存对象(ReferenceEntry)的。会把访问过的对象放到队列的最后。
并且可以很方便的更新和删除链表中的节点,因为每次访问的时候都可能需要更新该链表,放入到链表
的尾部。
这样,每次从access中拿出的头节点就是最久未使用的。
对应的writeQueue用来保存最久未更新的缓存队列,实现方式和accessQueue一样。
LoadingCache这些类表示获取Cache的方式,可以有多种方式,但是它们的方法最终调用到LocalCache的方法,LocalCache是Guava Cache的核心类
@GwtCompatible(emulated = true)
class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
LocalCache为Guava Cache的核心类 LocalCache的数据结构与ConcurrentHashMap很相似,都由多个segment组成,且各segment相对独立,互不影响,所以能支持并行操作
//Map的数组
final Segment<K, V>[] segments;
//并发量,即segments数组的大小
final int concurrencyLevel;
...
//访问后的过期时间,设置了expireAfterAccess就有
final long expireAfterAccessNanos;
//写入后的过期时间,设置了expireAfterWrite就有
final long expireAfterWriteNa就有nos;
//刷新时间,设置了refreshAfterWrite就有
final long refreshNanos;
//removal的事件队列,缓存过期后先放到该队列
final Queue<RemovalNotification<K, V>> removalNotificationQueue;
//设置的removalListener
final RemovalListener<K, V> removalListener;
....
每个segment由一个table和若干队列组成。缓存数据存储在table中,其类型为AtomicReferenceArray
static class Segment<K, V> extends ReentrantLock {
/**
* segments 维护一个entry列表的table,确保一致性状态。所以可以不加锁去读。节点的
next field是不可修改的final,因为所有list的增加操作
*/
final LocalCache<K, V> map;
/**
* 该segment区域内所有存活的元素个数
*/
volatile int count;
/**
* 改变table大小size的更新次数。这个在批量读取方法期间保证它们可以看到一致性的快照:
* 如果modCount在我们遍历段加载大小或者核对containsValue期间被改变了,然后我们会看
到一个不一致的状态视图,以至于必须去重试。
* count+modCount 保证内存一致性
* *
感觉这里有点像是版本控制,比如数据库里的version字段来控制数据一致性
*/
int modCount;
/**
* 每个段表,使用乐观锁的Array来保存entry The per-segment table.
*/
volatile AtomicReferenceArray<ReferenceEntry<K, V>> table; // 这里和
concurrentHashMap不一致,原因是这边元素是引用,直接使用不会线程安全
/**
* A queue of elements currently in the map, ordered by write time.
Elements are added to the tail of the queue
* on write.
*/
@GuardedBy("Segment.this")
final Queue<ReferenceEntry<K, V>> writeQueue;
/**
* A queue of elements currently in the map, ordered by access time.
Elements are added to the tail of the queue
* on access (note that writes count as accesses).
*/
@GuardedBy("Segment.this")
final Queue<ReferenceEntry<K, V>> accessQueue;
}
@GwtIncompatible
interface ReferenceEntry<K, V> {
/** Returns the value reference from this entry. */
ValueReference<K, V> getValueReference();
/** Sets the value reference for this entry. */
void setValueReference(ValueReference<K, V> valueReference);
/** Returns the next entry in the chain. */
@Nullable
ReferenceEntry<K, V> getNext();
/** Returns the entry's hash. */
int getHash();
/** Returns the key for this entry. */
@Nullable
K getKey();
/*
* Used by entries that use access order. Access entries are maintained in a doubly-linked list.
* New entries are added at the tail of the list at write time; stale entries are expired from
* the head of the list.
*/
/** Returns the time that this entry was last accessed, in ns. */
@SuppressWarnings("GoodTime")
long getAccessTime();
/** Sets the entry access time in ns. */
@SuppressWarnings("GoodTime") // b/122668874
void setAccessTime(long time);
/** Returns the next entry in the access queue. */
ReferenceEntry<K, V> getNextInAccessQueue();
/** Sets the next entry in the access queue. */
void setNextInAccessQueue(ReferenceEntry<K, V> next);
/** Returns the previous entry in the access queue. */
ReferenceEntry<K, V> getPreviousInAccessQueue();
/** Sets the previous entry in the access queue. */
void setPreviousInAccessQueue(ReferenceEntry<K, V> previous);
/*
* Implemented by entries that use write order. Write entries are maintained in a doubly-linked
* list. New entries are added at the tail of the list at write time and stale entries are
* expired from the head of the list.
*/
@SuppressWarnings("GoodTime")
/** Returns the time that this entry was last written, in ns. */
long getWriteTime();
/** Sets the entry write time in ns. */
@SuppressWarnings("GoodTime") // b/122668874
void setWriteTime(long time);
/** Returns the next entry in the write queue. */
ReferenceEntry<K, V> getNextInWriteQueue();
/** Sets the next entry in the write queue. */
void setNextInWriteQueue(ReferenceEntry<K, V> next);
/** Returns the previous entry in the write queue. */
ReferenceEntry<K, V> getPreviousInWriteQueue();
/** Sets the previous entry in the write queue. */
void setPreviousInWriteQueue(ReferenceEntry<K, V> previous);
}
缓存构建器。构建缓存的入口,指定缓存配置参数并初始化本地缓存。
主要采用builder的模式,CacheBuilder的每一个方法都返回这个CacheBuilder知道build方法的调用。
注意build方法有重载,带有参数的为构建一个具有数据加载功能的缓存,不带参数的构建一个没有数
据加载功能的缓存
build方法会创建LocalCache
public <K1 extends K, V1 extends V> LoadingCache<K1, V1> build(
CacheLoader<? super K1, V1> loader) {
checkWeightWithWeigher();
return new LocalCache.LocalLoadingCache<>(this, loader);
}
1、上锁
2、清除队列元素
清理的是keyReferenceQueue和valueReferenceQueue这两个队列,这两个队列是引用队列
如果发现key或者value被GC了,那么会在put的时候触发清理
3、setvalue方法了,它做的是将value写入Entry
@Override
public V put(K key, V value) {
checkNotNull(key);
checkNotNull(value);
int hash = hash(key);
return segmentFor(hash).put(key, hash, value, false);
}
Segment<K, V> segmentFor(int hash) {
// TODO(fry): Lazily create segments?
return segments[(hash >>> segmentShift) & segmentMask];
}
@Nullable
V put(K key, int hash, V value, boolean onlyIfAbsent) {
//保证线程安全加锁
lock();
try {
//获取当前时间
long now = map.ticker.read();
//根据过期,淘汰策略 清除列队中的元素
preWriteCleanup(now);
int newCount = this.count + 1;
if (newCount > this.threshold) {
// ensure capacity
expand();
newCount = this.count + 1;
}
//获取当前Entry中的HashTable的Entry数组
AtomicReferenceArray<ReferenceEntry<K, V>> table = this.table;
///定位 hash
int index = hash & (table.length() - 1);
获取第一个元素
ReferenceEntry<K, V> first = table.get(index);
遍历整个Entry链表判断是否存在
// Look for an existing entry.
for (ReferenceEntry<K, V> e = first; e != null; e = e.getNext()) {
K entryKey = e.getKey();
//判断hash,Value是否是同一个
if (e.getHash() == hash&& entryKey != null&& map.keyEquivalence.equivalent(key, entryKey)) {
// We found an existing entry.
//如果找到相应的元素
ValueReference<K, V> valueReference = e.getValueReference();
//获取所有的值 //获取value
V entryValue = valueReference.get();
//判断是否为空
if (entryValue == null) {
//如果entry的value为null,可能被GC掉了
++modCount;
if (valueReference.isActive()) {
//减小锁时间的开销
enqueueNotification(
key, hash, entryValue, valueReference.getWeight(), RemovalCause.COLLECTED);
//利用原来的key并且刷新value
//存储数据,并且将新增加的元素写入两个队列中,一个Write队列、一个Access队列
setValue(e, key, value, now);
newCount = this.count; // count remains unchanged
} else {
//存储数据,并且将新增加的元素写入两个队列
setValue(e, key, value, now);
newCount = this.count + 1;
}
// write-volatile,保证内存可见性
this.count = newCount; // write-volatile
//淘汰缓存
evictEntries(e);
return null;
} else if (onlyIfAbsent) {
//原来的Entry中包含指定key的元素,所以读取一次,读取操作需要更新Access队列
// Mimic
// "if (!map.containsKey(key)) ...
// else return map.get(key);
recordLockedRead(e, now);
return entryValue;
} else {
// clobber existing entry, count remains unchanged
++modCount;
enqueueNotification(
key, hash, entryValue, valueReference.getWeight(), RemovalCause.REPLACED);
//存储数据,并且将新增加的元素写入两个队列中
setValue(e, key, value, now);
//数据的淘汰
evictEntries(e);
return entryValue;
}
}
}
// Create a new entry.
++modCount;
//如果目标的entry不存在,那么新建entry
ReferenceEntry<K, V> newEntry = newEntry(key, hash, first);
setValue(newEntry, key, value, now);
table.set(index, newEntry);
newCount = this.count + 1;
this.count = newCount; // write-volatile
evictEntries(newEntry);
return null;
} finally {
//解锁
unlock();
//处理刚刚的remove Cause
postWriteCleanup();
}
}
V get(K key, CacheLoader<? super K, V> loader) throws ExecutionException {
int hash = hash(checkNotNull(key));
return segmentFor(hash).get(key, hash, loader);
}
V get(K key, int hash, CacheLoader<? super K, V> loader) throws ExecutionException {
//hash——>rehash
checkNotNull(key);
checkNotNull(loader);
try {
if (count != 0) {
// read-volatile
// don't call getLiveEntry, which would ignore loading values
ReferenceEntry<K, V> e = getEntry(key, hash);
if (e != null) {
long now = map.ticker.read();
V value = getLiveValue(e, now);
if (value != null) {
recordRead(e, now);
statsCounter.recordHits(1);
return scheduleRefresh(e, key, hash, value, now, loader);
}
ValueReference<K, V> valueReference = e.getValueReference();
if (valueReference.isLoading()) {
return waitForLoadingValue(e, key, valueReference);
}
}
}
// 如果取不到值,那么进行统一的加锁get
//加锁去数据源加载数据到缓存中
// at this point e is either null or expired;
return lockedGetOrLoad(key, hash, loader);
} catch (ExecutionException ee) {
Throwable cause = ee.getCause();
if (cause instanceof Error) {
throw new ExecutionError((Error) cause);
} else if (cause instanceof RuntimeException) {
throw new UncheckedExecutionException(cause);
}
throw ee;
} finally {
每次Put和get之后都要进行一次Clean
postReadCleanup();
}
}
数据过期不会自动重载,而是通过get操作时执行过期重载。具体就是CacheBuilder构造的LocalLoadingCache
static class LocalLoadingCache<K, V> extends LocalManualCache<K, V>
implements LoadingCache<K, V> {
LocalLoadingCache(
CacheBuilder<? super K, ? super V> builder, CacheLoader<? super K, V> loader) {
super(new LocalCache<K, V>(builder, checkNotNull(loader)));
}
// LoadingCache methods
@Override
public V get(K key) throws ExecutionException {
return localCache.getOrLoad(key);
}
@Override
public V getUnchecked(K key) {
try {
return get(key);
} catch (ExecutionException e) {
throw new UncheckedExecutionException(e.getCause());
}
}
@Override
public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException {
return localCache.getAll(keys);
}
@Override
public void refresh(K key) {
localCache.refresh(key);
}
@Override
public final V apply(K key) {
return getUnchecked(key);
}
// Serialization Support
private static final long serialVersionUID = 1;
@Override
Object writeReplace() {
return new LoadingSerializationProxy<>(localCache);
}
}
文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态
文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境
文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn
文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker
文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机
文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk
文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入
文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。 Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。
文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动
文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计
文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;gt;Jni-&amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图
文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法