0%

文章字数:101,阅读全文大约需要1分钟

类似qq空间图片点击放大(灯箱效果)

引入

1
2
<script type="text/javascript" src="js/lightbox/lightbox.min.js"></script>
<link href="js/lightbox/css/lightbox.css" rel="stylesheet" />

单张图片

1
2
3
<a href="img/image-1.jpg" data-lightbox="img1" data-title="图片一">
<img src="xxx" title="缩略图">
</a>

图片组(组内可以切换)

1
2
3
4
<a href="img/image-1.jpg" data-lightbox="group2" data-title="图片标题一">缩略图...</a>
<a href="img/image-2.jpg" data-lightbox="group2" data-title="图片标题二">缩略图...</a>
<a href="img/image-3.jpg" data-lightbox="group2" data-title="图片标题三">缩略图...</a>
<a href="img/image-4.jpg" data-lightbox="group2" data-title="图片标题四">缩略图...</a>

文章字数:734,阅读全文大约需要2分钟

AbstractQueuedSynchronizer它提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架。大部分同步工具内部都是使用AQS实现的。

一、LockSupport

jdk提供的线程阻塞、唤醒的工具。同步组件的基础

  • park开头的方法是用于阻塞线程的
  • unpark(Thread thread)方法是解除阻塞的

二、模板方法模式

  • 父类使用抽象类,定义若干动作,并且按顺序调用。
  • 子类实现具体动作
  • AQS里使用了模板方法模式

三、AQS中的抽象模板

3.1独占式获取锁

-accquire

  • acquireInterruptibly
  • tryAcquireNanos

    3.2共享式获取锁

  • acquireShared
  • acquireSharedInterruptibly
  • tryAcquireShareNanos

    3.3独占式释放锁

  • release

3.4共享式释放锁

  • releaseShared

3.5子类需要实现的过程方法

  • tryAcquire独占获取,内部判断哪个线程拿到了锁,然后setExclusiveOwnerThread(Thread.currentThread())设置当前线程占有锁,return true
  • tryRealease独占释放
  • tryAcquireShare共享获取
  • tryRealeaseShared共享释放
  • isHeldExclusively这个同步器释放处于独占模式(是否占用)

3.6同步状态

  • state:字段表示当前同步状态,使用volatile修饰。
  • getState:获取
  • setState:设置当前同步状态
  • compareAndSetState:使用CAS设置状态

3.6Condition

  • AQS提供了ConditionObject直接new返回就行了。
  • 内部维护了一个等待队列,保存等待的线程

四、AQS公平非共享内部实现

  • 内部使用一个先进先出的双向队列保存线程,保存了链表的头和尾
  • Node类就是线程节点
    节点类型waitState
  1. CANCELLED线程等待超时或者被中断,需要从队列移走
  2. SIGNAL后续的节点等待状态,当前节点通知后面节点运行
  3. CONDITION等待
  4. PEOPGATE共享状态,表示状态往后面传播
  5. 0初始状态
  • 竞争锁失败的线程被打包成Node放入同步队列尾部(CAS设置),如果上一个节点是头节点时会尝试获取锁,获取失败阻塞自己。成功会移除头节点,设置自身为头。Realsese方法会唤醒阻塞。
  • Condition中包含一个等待队列

文章字数:459,阅读全文大约需要1分钟

AbstractQueuedSynchronizer维护了一个State值和一个FIFO等待队列的框架。可以用来实现阻塞锁和同步器,ReentrantLock等锁就是基于AQS实现的

State

共享资源变量,int类型。访问方式有

  1. getState()
  2. setState(int newState)
  3. compareAndSetState(int expect, int update): 依赖于UnsafecompareAndSwapInt()方法

三种方式都是原子操作。
AQS将大部分的同步逻辑均已经实现好,继承的自定义同步器只需要实现state的获取(acquire)和释放(release)的逻辑代码就可以,主要包括下面方法:

  1. tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
  2. tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
  3. tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  4. tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
  5. isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。

CLH队列(FIFO)

使用内部类Node来实现,其中维护一个变量waitStatus代表当前节点代表状态

  1. acquire(int arg):独占模式获取资源
  2. release(int arg):独占模式释放资源
  3. acquireShared(int arg):共享模式获取
  4. releaseShared(int arg):共享模式释放

文章字数:40,阅读全文大约需要1分钟

ArrayList 提示 All elements are null ,size显示为1,但是内容为空。

ArrayList允许添加空值null,所以显示All elements are null但是size显示还是1。


文章字数:96,阅读全文大约需要1分钟

SpringMVC的错误

现象

  1. 新加了一个URL映射,运行之后提示此错误

解决

  1. 造成的原因是URL映射和之前的重复,导致请求到达时无法判断使用那个方法处理。错误提示中{}包裹的部分就是冲突的两个方法
  2. 修改映射路径即可解决

文章字数:569,阅读全文大约需要2分钟

Apache Ignite是一款以内存为中心的分布式数据库、缓存和处理平台。可以在PB级数据中,以内存级的速度进行事务性、分析性以及流式负载的处理。使用springBoot开发,支持完整的sql(部分函数不支持)

和redis对比

  • Ignite提供了完整的SQL、DDL和DML的支持,可以使用纯SQL而不用写代码与Ignite进行交互,这意味着只使用SQL就可以创建表和索引,以及插入、更新和查询数据。有这个完整的SQL支持,Ignite就可以作为一种分布式SQL数据库。

  • Ignite的内存数据网格组件是一个完整的事务型分布式键值存储,它可以在有几百台服务器的集群上进行水平扩展。在打开持久化时,Ignite可以存储比内存容量更大的数据,并且在整个集群重启之后仍然可用。

  • 和Redis 等NoSQL数据库一样,Ignite支持高可用和水平扩展,但是,和Redis数据库不同,Ignite支持SQL和ACID。


文章字数:529,阅读全文大约需要2分钟

ArrayList内部使用数组保存元素,所以查找添加和查找的速度都很快。但是达到一定的数量需要扩容,而扩容是开销很大的操作。如果需要频繁插入和删除元素,可以使用LinkedList

特性

  1. 线程不安全,即可以同时被多个线程操作。可能会导致数据读和写操作直接数据被篡改。可以使用Collections.synchronizedList(List l)获取一个线程安全的ArrayList。或者使用jucCopyOnWriteArrayList

  2. 实现了Serializable可以序列化

  3. 初始化大小为10,当元素数量超出这个大小时会触发扩容。扩容大小为原来的1.5倍。

  4. 内部使用private transient Object[] elementData数组存储信息。

扩容过程

  1. 初始化创建指定大小的数组,不写默认10

  2. 插入元素时检测长度是否超出当前数组最大值

  3. 超出新建一个数组,大小为原来的1.5倍

  4. 把原数组的信息迁移到新的数组上。
    数组迁移内部使用了Arrays.copyof(),这个方法新建了一个数组并使用System.arraycopy()方法复制信息到新数组。
    System.arraycopy()native方法,最后使用的是c的memmove()函数,所以效率很高

使用建议

  1. 能够确定存储信息数量时最好指定初始化大小,避免多次触发扩容操作。

文章字数:82,阅读全文大约需要1分钟

Arrays.asList将数组转换成列表,但是Arrays.asList返回的List是一个匿名内部类,而不是ArrayList。

可以使用set代替add

内部ArrayList的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;

ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}

@Override
public int size() {
return a.length;
}

@Override
public Object[] toArray() {
return a.clone();
}

@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}

@Override
public E get(int index) {
return a[index];
}

@Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}

@Override
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}

@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}

@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(a, Spliterator.ORDERED);
}

@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (E e : a) {
action.accept(e);
}
}

@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
E[] a = this.a;
for (int i = 0; i < a.length; i++) {
a[i] = operator.apply(a[i]);
}
}

@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
}

文章字数:420,阅读全文大约需要1分钟

Spring最基本的功能就是容器,容器的核心功能是IOC控制反转。将类的创建、装配、销毁交由容器管理。BeanFacotryApplicationContext两个接口是Spring最核心的接口,其中ApplicationContextBeanFactory的子接口。二者都可以作为容器使用。

关系

  1. ApplicationContextBeanFactory的拓展接口。

  2. BeanFactory一般称为IOC容器,而ApplicationContext称为应用上下文。

  3. BeanFactory提供了最基本的容器管理功能,ApplicationContext提供更多服务,如解析配置文本信息等。一般使用ApplicationContext

BeanFactory

主要功能是装配,创建,管理Bean。
Spring ICO容器的实现本质上是BeanFactory,但是真正可以单独使用的容器还是DefaultListableBeanFactory(实现类)

  1. BeanFactory是容器的根接口,主要方法是getBean(String beanName)

  2. 直接子接口

  • HierarchicalBeanFactory:提供父容器的访问功能
  • ListableBeanFactory:提供批量获取Bean的方法
  • AutowireCapableBeanFactory:增加对已存在的实例的管理
  1. ConfigurableBeanFactory
    主要单例bean的注册,生成实例,以及统计单例bean

  2. ConfigurableListableBeanFactory
    继承了上述的所有接口,增加了其他功能:比如类加载器,类型转化,属性编辑器,BeanPostProcessor,作用域,bean定义,处理bean依赖关系, bean如何销毁…

  3. 实现类DefaultListableBeanFactory
    实现了ConfigurableListableBeanFactory,实现上述BeanFactory所有功能。它还可以注册BeanDefinition

ApplicationContext

应用上下文,继承了BeanFactory接口拥有更多的功能。启动时就可以加载所有的Bean,也可以延迟加载。

  1. 国际化(MessageSource)
  2. 资源访问(ResourceLoader)
  3. 载入多个上下文
  4. 消息发送响应机制(ApplicationEventPublisher)
  5. aop

web端使用ApplicationContext多一点


文章字数:309,阅读全文大约需要1分钟

Java Attach是一种让java进程之间进行通信的机制。比如jvm运行时获取信息的jstack工具就是应用Attach的。

作用

  1. 获取信息: 通过Attach机制可以获取jvm的内存dump、线程dump、类统计信息、获取vm flag、获取系统属性等。
  2. 动态控制程序: 为jvm进程动态加载agent,动态设置vm flag(运行时的flag)

简单使用

  1. 使用com.sun.tools.attach下的相关类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws IOException, AttachNotSupportedException {
VirtualMachine attach = VirtualMachine.attach("1234");//需要连接jvm进程的pid
List<VirtualMachineDescriptor> list = VirtualMachine.list();
InputStream in = ((HotSpotVirtualMachine) attach).remoteDataDump((Object[]) args);
byte b[] = new byte[256];
int n = 0;
do {
n = in.read(b);
if (n > 0) {
System.out.println(new String(b, 0, n));
}
} while (n > 0);
in.close();
attach.detach();
}
  1. 相关jvm参数
jvm参数 默认值 介绍
DisableAttachMechanism false 禁止Attach
StartAttachListener false 程序启动时就开启Attach Listener线程,否则需要Signal Dispatcher线程唤醒
ReduceSignalUsage false 减少信号量?

原理

  1. 每个jvm都有两个线程: Signal Dispatcher处理信号,Attach Listener用于jvm进程间通信。

  2. Attach Listener线程默认不开启,使用jvm参数可以启动时开启。否则会在Attach连接成功后开启。

  3. Attach Listener线程启动后,就会创建一个监听套接字,并创建了一个文件/tmp/.java_pid

  4. 客户端向java_pid中写相关命令,Attach Listener线程会监听这个文件,有信息就解析,执行,返回。