-
-
Save steven-zou/7ce14e01d2ad7cd07091 to your computer and use it in GitHub Desktop.
| 引用计数法(Reference Counting): | |
| 1. 有引用,计数器+1, 引用失效,则计数器-1 | |
| 2. 实现简单,判定效率高 | |
| 3. 未在主流虚拟机中使用,因为解决不了循环引用的问题 | |
| 可达性分析(Reachability Analysis): | |
| 1. 以GC root对象作为起点,向下搜索 | |
| 2. 所走过的路径为引用链, | |
| 3. 当一个对象到GC root 没有任何的引用链的时候,则不可达,可以回收 | |
| 4. 可作为GC root的对象 | |
| 4.1 虚拟机栈中引用对象 | |
| 4.2 方法区中的静态属性引用的对象 | |
| 4.3 方法区中的常量引用对象 | |
| 4.4 本地方法栈中的JNI引用对象 | |
| 垃圾收集算法: | |
| 1. 标记-清除 (Mark-Sweep) | |
| 问题:效率不高,会产生大量不连续的内存碎片 | |
| 2. 复制算法 | |
| 等大的内存块,讲存活的对象复制到另外一块,再把已使用过的内存空间一次清理掉 | |
| 现代商业虚拟机都依靠此技术来回收新生代 | |
| 问题:如果对象存活率较高时,进行太多的复制,效率变低 | |
| 3. 标记-整理算法 | |
| 先标记,之后所有存活对象向一边移动,然后直接清理掉端边界以外的内存 | |
| 现代商业虚拟机都采用的是分代收集算法 | |
| Java Thread <-> Work memory | | | |
| | | | |
| Java Thread <-> Work memory <-> | Save &Load | Major Memory | |
| | | | |
| Java Thread <-> Work memory | | | |
| Memory operations: | |
| lock -> major memory | |
| unlock-> major memory | |
| read -> major | |
| load -> work memory | |
| use -> work | |
| assign -> work | |
| store ->work | |
| write -> major |
| http://www.360doc.com/content/12/0113/08/1073512_179088229.shtml |
| 用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。 | |
| Read Uncommitted(读取未提交内容) | |
| 在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未 | |
| 提交的数据,也被称之为脏读(Dirty Read)。 | |
| Read Committed(读取提交内容) | |
| 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔 | |
| 离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能 | |
| 返回不同结果。 | |
| Repeatable Read(可重读) | |
| 这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题: | |
| 幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时 | |
| ,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。 | |
| Serializable(可串行化) | |
| 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个 | |
| 级别,可能导致大量的超时现象和锁竞争。 | |
| References: http://xm-king.iteye.com/blog/770721 |
| OAuth 2.0 的基本流程 | |
| OAuth 2.0 相对于1.0的主要改进 |
| TCP连接是面向连接的,所谓的面向连接就是,当计算机双向通信时必需先建立连接,然后才能进行数据的传输,最后还要拆除连接。而同在一个 | |
| 网络层的UDP传输,是面向非连接的传输,也不是可靠的。 | |
| 位码即tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急) | |
| Client <------------> Server | |
| ->SYN=j | |
| SYN=J+1&ACK=K<- | |
| ->ACK=K+1 | |
| 实例: | |
| IP 192.168.1.116.3337 > 192.168.1.123.7788: S 3626544836:3626544836 | |
| IP 192.168.1.123.7788 > 192.168.1.116.3337: S 1739326486:1739326486 ack 3626544837 | |
| IP 192.168.1.116.3337 > 192.168.1.123.7788: ack 1739326487,ack 1 | |
| 第一次握手:192.168.1.116发送位码syn=1,随机产生seq number=3626544836的数据包到192.168.1.123,192.168.1.123由SYN=1知道 | |
| 192.168.1.116要求建立联机; | |
| 第二次握手:192.168.1.123收到请求后要确认联机信息,向192.168.1.116发送ack number=3626544837,syn=1,ack=1,随机产生seq=1739326486 | |
| 的包; | |
| 第三次握手:192.168.1.116收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确, | |
| 192.168.1.116会再发送ack number=1739326487,ack=1,192.168.1.123收到后确认seq=seq+1,ack=1则连接建立成功。 | |
| 由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来 | |
| 终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭 | |
| 的一方将执行主动关闭,而另一方执行被动关闭。 | |
| Client <------------> Server | |
| -->FIN | |
| ACK<-- | |
| CLOSE< | |
| FIN<-- | |
| -->ACK | |
| >CLOSE | |
| ======================= | |
| CLOSED: 这个没什么好说的了,表示初始状态。 | |
| LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。 | |
| SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态, | |
| 很短暂,基本 上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。 | |
| 因此这种状态 时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。 | |
| SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态, | |
| 并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。 | |
| ESTABLISHED:这个容易理解了,表示连接已经建立了。 | |
| FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别 是: | |
| FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即 进入到FIN_WAIT_1状态。 | |
| 而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马 上回应ACK报文,所以 | |
| FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。 | |
| FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方, | |
| 我暂时还有点数据需要传送给你,稍后再关闭连接。 | |
| TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时 | |
| 带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。 | |
| CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先 | |
| 收到(或同时收到)对方的 ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收 | |
| 到了对方的FIN报文。什 么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么 | |
| 就出现了双方同时发送FIN报 文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。 | |
| CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个 | |
| ACK报文给对 方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么 | |
| 你也就可以 close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。 | |
| LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到 | |
| CLOSED可用状态了。 | |
| ====================== | |
| 1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢? | |
| 这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。 | |
| 但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上 | |
| 会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文 | |
| 多数情况下都是分开发送的. | |
| 2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态? | |
| 这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样); | |
| 但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未 | |
| 收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。 |
| Weak references | |
| A weak reference, simply put, is a reference that isn't strong enough to force an object to remain in memory. Weak references | |
| allow you to leverage the garbage collector's ability to determine reachability for you, so you don't have to do it yourself. | |
| You create a weak reference like this: | |
| ===================================================== | |
| WeakReference weakWidget = new WeakReference(widget); | |
| ===================================================== | |
| and then elsewhere in the code you can use weakWidget.get() to get the actual Widget object. Of course the weak reference | |
| isn't strong enough to prevent garbage collection, so you may find (if there are no strong references to the widget) | |
| that weakWidget.get() suddenly starts returning null. | |
| Soft references | |
| A soft reference is exactly like a weak reference, except that it is less eager to throw away the object to which it refers. | |
| An object which is only weakly reachable (the strongest references to it are WeakReferences) will be discarded at the next | |
| garbage collection cycle, but an object which is softly reachable will generally stick around for a while. | |
| SoftReferences aren't required to behave any differently than WeakReferences, but in practice softly reachable objects are | |
| generally retained as long as memory is in plentiful supply. This makes them an excellent foundation for a cache, such as the | |
| image cache described above, since you can let the garbage collector worry about both how reachable the objects are | |
| (a strongly reachable object will never be removed from the cache) and how badly it needs the memory they are consuming. | |
| Soft Reference 虽然和 Weak Reference 很类似,但是用途却不同。 被 Soft Reference 指到的对象,即使没有任何 Direct Reference, | |
| 也不会被清除。一直要到 JVM 内存不足时且 没有 Direct Reference 时才会清除,SoftReference 是用来设计 object-cache 之用的。 | |
| 如此一来 SoftReference 不但可以把对象 cache 起来,也不会造成内存不足的错误 (OutOfMemoryError)。我觉得 Soft Reference | |
| 也适合拿来实作 pooling 的技巧。 | |
| Comments: | |
| ----------- | |
| The Sun JRE does treat SoftReferences differently from WeakReferences. We attempt to hold on to object referenced by a | |
| SoftReference if there isn't pressure on the available memory. One detail: the policy for the | |
| "-client" and "-server" JRE's are different: the -client JRE tries to keep your footprint small by | |
| preferring to clear SoftReferences rather than expand the heap, whereas the -server JRE tries to keep your performance | |
| high by preferring to expand the heap (if possible) rather than clear SoftReferences. One size does not fit all. | |
| Strong referebce(强引用): 默认的引用形式,只要引用存在,则永远不会被回收 | |
| Phantom Reference: 虚引用,完全不会对其生存时间构成影响,唯一的目的就是能在这个对象被收集器回收时收到一个系统通知 |
Java内存模型允许虚拟机对64位数据类型(long&double)的读写操作划分为两次32位操作进行,即不保证load、store、read和write的原子性,这就是所谓的非原子性协定。
实际上虚拟机的实现中,一般保证了long和double的原子性操作
符合Java happens-before 先行发生原则的操作序列,则不需要任何同步手段即可保证线程安全
线程实现:
- 基于内核线程(使用内核线程高级接口 LWP, 轻量级进程),LWP与内核线程是1:1. 这种模式下,所有的线程操作都需要系统调用(需要再user mode和kernel mode之间切换)来完成,代价颇高,因而支持的数量有限。
- 基于用户线程实现。 直接在用户态中完成,不需要切换到内核态。和进程之间可以实现1:N的对应。缺点就是无法利用系统内核,实现复杂,代价大。
- 用户进程+LWP 混合实现N:M的对应关系. (P380)
线程安全:
当多个线程访问一个对象的时候,如果不考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象的线程就是安全的。
-- Brian Goetz
synchronized明确指定了对象参数,那就是这个对象的reference;如果没有明确指定,那就根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或者class对象来作为锁对象。
synchronized的锁为非公平锁,ReentrantLock默认也是非公平锁,可以通过带bool值的构造函数来要求使用公平锁。(公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁)
上述都是互斥同步,也即阻塞同步
避免阻塞,可以使用自旋(锁)
在编译阶段,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。
常见配置汇总
堆设置
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
新生代的划分:
SUN/Oracle 的HotSpot JVM 又把新生代进一步划分为3个区域:一个相对大点的区域,称为”伊甸园区(Eden)”;两个相对小点的区域称为”From 幸存区(survivor)”和”To 幸存区(survivor)”。按照规定,新对象会首先分配在 Eden 中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden 中的对象会被移动到survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。
基于大多数新生对象都会在GC中被收回的假设。新生代的GC 使用复制算法。在GC前To 幸存区(survivor)保持清空,对象保存在 Eden 和 From 幸存区(survivor)中,GC运行时,Eden中的幸存对象被复制到 To 幸存区(survivor)。针对 From 幸存区(survivor)中的幸存对象,会考虑对象年龄,如果年龄没达到阀值(tenuring threshold),对象会被复制到To 幸存区(survivor)。如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From 幸存区中只保存死对象,可以视为清空。如果在复制过程中To 幸存区被填满了,剩余的对象会被复制到老年代中。最后 From 幸存区和 To幸存区会调换下名字,在下次GC时,To 幸存区会成为From 幸存区。
PRODUCER-CONSUMER Problem:
public interface IStorage {
void produce();
void consume();
}
import java.util.ArrayList;
import java.util.List;
public class Storage implements IStorage{
private final int MAX_COUNT;
private final List<Good> l;
public Storage(int maxCount){
MAX_COUNT = maxCount <=0?10:maxCount;
l = new ArrayList<Good>(MAX_COUNT);
}
public void produce(){
synchronized(l){
while(l.size() == MAX_COUNT){
System.out.println("Storage is full! Waiting for consuming...");
try {
l.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
l.add(new Good(l.size()));
System.out.println("Current size[P]:"+l.size());
l.notifyAll();
}
}
public void consume(){
synchronized(l){
while(l.size() == 0){
System.out.println("Storage is empty! Waiting for producing");
try {
l.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
l.remove(0);
System.out.println("Current size[C]:"+l.size());
l.notifyAll();
}
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class CStorage implements IStorage {
private final int MAX_COUNT;
private final List<Good> l;
private final Lock lock;
private final Condition signal;
public CStorage(int maxCount){
MAX_COUNT = maxCount<=0?10:maxCount;
l = new ArrayList<Good>(MAX_COUNT);
lock = new ReentrantLock(true);
signal = lock.newCondition();
}
@Override
public void produce() {
lock.lock();
while(l.size() == MAX_COUNT){
System.out.println("Storage is full! Waiting for consuming...");
try {
signal.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
l.add(new Good(l.size()));
System.out.println("Current size[P]:"+l.size());
signal.signalAll();
lock.unlock();
}
@Override
public void consume() {
lock.lock();
while(l.size() == 0 ){
System.out.println("Storage is empty! Waiting for producing");
try {
signal.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
l.remove(0);
System.out.println("Current size[C]:"+l.size());
signal.signalAll();
lock.unlock();
}
}
public class Producer implements Runnable {
private final IStorage store;
public Producer(IStorage store){
this.store = store;
}
@Override
public void run() {
while(true){
store.produce();
}
}
}
public class Consumer implements Runnable {
private final IStorage store;
public Consumer(IStorage store){
this.store = store;
}
@Override
public void run() {
while(true){
store.consume();
}
}
}
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
//Storage store = new Storage(10);
CStorage store = new CStorage(10);
for(int i=0;i<5;i++){
new Thread(new Producer(store),"P"+i).start();
}
for(int i=0;i<3;i++){
new Thread(new Consumer(store),"C"+i).start();
}
}
}
PRODUCER-CONSUMER Problem part2:
private LinkedBlockingQueue list = new LinkedBlockingQueue(MAX_COUNT);
put()方法:类似于我们上面的生产者线程,容量达到最大时,自动阻塞。
take()方法:类似于我们上面的消费者线程,容量为0时,自动阻塞。
排序算法分析:
Sort Methods | Average Time | Worst Time | Space
简单排序 O(n^2) O(n^2) O(1)
快速排序 O(nlogN) O(n^2) O(logN)
堆排序 O(nlogN) O(nlogN) O(1)
归并排序 O(nlogN) O(nlogN) O(n)
基数排序 O(d(n+rd)) O(d(n+rd)) O(rd)
几个排序算法:
public void StraightInsertionSort(int[] arr){
for(int i=1;i<arr.length;i++){
if(arr[i-1]>arr[i]){
int temp = arr[i];
int j=i-1;
for(;j>=0&&temp<arr[j];j--){
arr[j+1] = arr[j];
}
arr[j+1] = temp;
}
}
}
public void shellSort(int[] arr){
double dk = arr.length;
int temp = 0;
while(true){
dk = Math.ceil(dk/2);
int d = (int)dk;
for(int x=0;x<d;x++){
for(int i=x+d;i<arr.length;i+=d){
if(arr[i-d]>arr[i]){
temp = arr[i];
int j=i-d;
for(;j>=0&&temp<arr[j];j-=d){
arr[j+d] =arr[j];
}
arr[j+d] = temp;
}
}
}
if(d==1){
break;
}
}
}
public void selectionSort(int[] arr){
for(int i=0,len=arr.length;i<len;i++){
int temp=arr[i], position=i;
for(int j=i+1;j<len;j++){
if(arr[j]<temp){
temp=arr[j];
position=j;
}
}
arr[position] =arr[i];
arr[i] = temp;
}
}
public void quickSort(int[] arr){
if(arr!=null&&arr.length>0){
quickSort(arr,0,arr.length-1);
}
}
private void quickSort(int[] arr, int low, int high){
if(low<high){
int pv = partition(arr,low,high);
quickSort(arr,low,pv-1);
quickSort(arr,pv+1,high);
}
}
private int partition(int[] arr, int low, int high){
int pv = arr[low];
while(low<high){
while(low<high&&arr[high]>=pv){
high--;
}
arr[low]=arr[high];
while(low<high&&arr[low]<=pv){
low++;
}
arr[high]=arr[low];
}
arr[low]=pv;
return low;
}
ORM:
ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射.
Drawbacks:
- 自动化意味着映射和关联管理,代价是牺牲性能
- 面向对象的查询语言(X-QL)不能完全的屏蔽掉数据库层的设计,增加学习成本
- 对于复杂查询,ORM仍然力不从心
OOP 四大特征 "抽象"、"封装"、"继承"、"多态"
OOP 四大原则:
- SRP 单一职责原则: 一个类,应该仅有一个引起它变化的原因
- 开放封闭原则: 软件实体(类、模块、函数等)应该可以扩展,但不可修改
- 依赖倒转原则: 抽象不应该依赖细节,细节应该依赖抽象(面向接口编程)
- 里氏代换原则: 子类型必须能够替换掉他们的父类型
AOP技术的具体实现,无非也就是通过动态代理技术或者是在程序编译期间进行静态的"织入"方式。下面是这方面技术的几个基本术语: 1、join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。 2、point cut(切入点):本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。 3、advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑。 4、aspect(方面):point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。 5、introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的 OP工具又将其称为mixin。
JDK动态代理:
包含一个类和一个接口:
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数
可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
参数说明:
ClassLoader loader:类加载器
Class[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
Ps:类加载器
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器;
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。
volatile关键字:
volatile只保证变量的可见性
使用volatile关键字标示的变量也不一定是线程安全的,比如和非volatile的变量共同参与不变约束,比如运算结果依赖当前变量的值
即使编译出来的字节码只有一行,也不能代表这条字节码指令是原子的
volatile禁止指令优化重排序
多核CPU需要通过内存屏障指令来保证指令执行顺序