Java笔记二

来源:互联网 发布:企业网站搜索优化 编辑:程序博客网 时间:2024/06/02 06:44

1.

Java集合框架之ConcurrentHashMap


HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是:所有访问HashTable的线程都必须竞争同一把锁。那么假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率。这就是ConcurrentHashMap所使用的锁分段技术。首先,将数据分成一段一段的存储,然后给每一段数据分配一把锁,当一个线程占用锁访问其中一个数据段的时候,其他的数据也能被其他线程访问。


ConcurrentHashMap的结构:

由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色。HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,一个Segment数组里包含一个HashEntry数组。Segment的结果和HashMap类似,是一种数组和链表结构,HashEntry是一种链表结构的元素。

一个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。


2.

java反射机制可以让我们在运行期检查类,接口,变量以及方法的信息。还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法获取变量的值。

在想检查一个类的信息之前,首先需要获取类的Class对象。


在通常的观点中从对象的外部访问私有变量以及方法是不允许的,但是java反射机制可以做到这一点。


3.

Java有三个内建的Annotations。

@Deprecated

@Override

@SuppressWarnings


4.

利用java反射机制可以在运行期动态地创建接口的实现。java,lang.reflect.Proxy类就可以实现这一功能。可以通过使用Proxy.newProxyInstance()方法创建动态代理。newProxyInstance()方法有三个参数:

1.类加载器(ClassLoader)用来加载动态代理类

2.一个要实现的接口数组

3.一个InvocationHandler把所有方法的调用都转移到代理上。

InvocationHandler handler = new MyInvocationHandler();        MyInterface proxy = Proxy.newProxyInstance(MyInterface.class.getClassLoader(),new Class[]{                MyInterface.class        },handler);
执行完后,变量proxy包含一个MyInterface接口的动态实现。所有对proxy的调用都被转向到实现了InvocationHandler接口的Handler上。

所有对动态代理对象的方法调用都会被转向到了InvocationHandler接口的实现上。


5.

要提到并发,自然就要提到锁,通过使用锁,使得多线程的并发控制变得十分简单。但是付出的代价也很高,只有获取到锁的线程才能够执行代码,而其他线程必须挂起等待直到锁被释放,这期间它不能做任何事情。并且,在线程进行切换的过程中,即一个线程释放锁,另一个线程被调度获得锁并执行代码,也存在着很大的系统开销。然而人们对程序效率的追求并没有止步,程序的响应能不能更快一点、效率能不能更高一点等问题不断的激发着人们的热情。于是就产生了多种不同种类的锁,分别适用于在不同的场景下提高程序的并发效率。

乐观锁和悲观锁:

它们的区别在于悲观锁假设最坏的情况一定会发生,所以就在每次访问共享资源时都上锁。然而我们知道并不是所有的并发操作都会导致数据不一致,这就导致有些本来可以并发的线程由于不能获得锁而必须等待,从而降低了并发的效率。

乐观锁,就像它的名字一样,它以一种乐观的态度去访问共享数据,即它认为对共享数据的修改不会造成冲突,所以访问共享资源的时候并不加锁,如果它对共享资源的修改真的产生了冲突,那么它就会放弃这次修改,然后不管重试。


读写锁:

读写锁是一种锁分离技术,它把读锁和写锁分开,读锁可以被多个线程持有,这样读线程就可以并发,而写锁是互斥的,它只能被一个线程持有,并且写锁和读锁也互斥。


可重入锁:

synchronized就是可重入的,可重入锁就是说一个线程获取到锁后,在该线程内部又要递归的获取锁,如果锁不是可重入的,就会造成死锁,因为它不能获取到已经被自己保持的锁。可重入锁就是说一个线程内部递归的获取锁时,会使锁计数器加1,该线程没释放一个锁,锁的计数器就减1,当锁计数器为0时,锁就被完全释放。ReentrantLock


非阻塞同步算法:CompareAndSwap,乐观锁技术。

CompareAndSwap有三个操作数:内存值V,预期值A,新值B。当使用CompareAndSwap时,会先将预期值A与内存值V进行比较,如果相同,则把内存值V替换成新值B,如果不相同,则不进行替换,并返回内存中的实际值。这个语义可以解释为:我认为V的值应该为A,如果是,就把V的值替换成B,如果不是就不替换,并告诉我内存中的实际值。在JDK的原子类中都提供了这种基于乐观锁的CAS操作,而且concurrent包中的很多类也使用了这些原子类。在这些原子类中通过调用sun.misc.Unsafe里面的CAS算法,用CPU指令来实现无锁自增。


6.

内部类:

内部类的特点:

独立的class文件

同样需要编译

同样可以产生对象

静态内部类:

class Outer {

static class Inner {

//静态内部类不能访问到外部的成员,除非外部成员也是静态成员

}

}

静态内部创建对象:a.用外部类。b.导入内部类所在的包。c.用全限定名


C.成员内部类

class Outer {

class Inner{

//成员内部类可以访问到外部的成员,不能有静态变量和方法

}

}


成员内部类创建对象:Outer outer = new Outer();

Outer.Inner inner = ouer.new Inner();


D.局部内部类:只能在方法内部使用

class Outer {

void fd() {

class Inner{

//局部内部类

}

}

}


E.匿名内部类:


对于一个名为OuterClass的外部类和InnerClass的内部类,在编译成功后,会出现两个class文件:OuterClass.class和OuterClass$InnerClass.class


7.

HashSet类的特点:能够快速定位集合中的元素,集合中的元素无序。有了HashMap的实现,则HashSet的实现非常简单,只需要将Set中的元素作为Map的key值来保存即可。


8.

在Java中,能够表示数值的数据类型主要分为两类,一类是:float和double,,另一类是:byte,short,int和long。这些基本类型对应的封装类都继承了Number类。


public abstract class NumberDemo implements java.io.Serializable{    public abstract int intValue();    public abstract long longValue();    public abstract float floatValue();    public abstract double doubleValue();    public byte byteValue() {        return (byte)intValue();    }    public short shortValue() {        return (short)intValue();    }}

几乎所有继承Number类的都对如上定义的几个方法进行了重新的实现。

private static class ByteCache {        private ByteCache(){        }        static final Byte cache[] = new Byte[-(-128)+127+1];        static  {            for (int i = 0; i < cache.length; i++) {                cache[i] = new Byte((byte)(i-128));            }        }    }    public static Byte valueOf(byte b) {        final int offset = 128;        return ByteCache.cache[(int)b+offset];    }

一个静态私有的内部类中定义了一个非常重要的Byte数组cache[]。这个cache在什么时候进行初始化呢?是在ByteCache静态类加载的时候,通过运行static静态块来完成的。那么这个静态内部类到底在什么时候加载呢?肯定是在valueOf()方法中调用的时候。

其实,数值类型中除了Float和Double类型外,Byte,Short,Integer,Long,Character,Boolean都实现了这种数值的共享,并且也都是-127~128区间值的缓存,而对于不在这个区间范围内的值,只能new一个对应的包装类型进行返回了

0 0
原创粉丝点击