synchronized关键字加锁的原理,其实是对对象加锁,不论你是在方法前加synchronized还是语句块前加,锁住的都是对象整体,但是ConcurrentHashMap的同步机制和这个不同,它不是加synchronized关键字,而是基于lock操作的,这样的目的是保证同步的时候,锁住的不是整个对象。ConcurrentHashMap是基于一个叫Segment数组的.
1、HashMap和HashTable
相同点:二者都实现了Map接口,因此具有一系列Map接口提供的方法。
不同点:
1、HashMap继承了AbstractMap,而HashTable继承了Dictionary。
2、HashMap非线程安全,HashTable线程安全,到处都是synchronized关键字。
3、因为HashMap没有同步,所以处理起来效率较高。
4、HashMap键、值都允许为null,HashTable键、值都不允许有null。
5、HashTable使用Enumeration,HashMap使用Iterator。
这些就是一些比较突出的不同点,实际上他们在实现的过程中会有很多的不同,如初始化的大小、计算hash值的方式等等。毕竟这两个类包含了很多方法,有很重要的功能,所以其他不同点,请感兴趣的读者自己去看源码,去研究。笔者推荐使用HashMap,因为她提供了比HashTable更多的方法,以及较高的效率,如果大家需要在多线程环境中使用,那么用Collections类来做一下同步即可。
2、Set接口和List接口
相同点:都实现了Collection接口
不同点:
1、Set接口不保证维护元素的顺序,而且元素不能重复。List接口维护元素的顺序,而且元素可以重复。
2、关于Set元素如何保证元素不重复,我将在下面的博文中给出。
3、ArrayList和LinkList
相同点:都实现了Collection接口
不同点:ArrayList基于数组,具有较高的查询速度,而LinkedList基于双向循环列表,具有较快的添加或者删除的速度,二者的区别,其实就是数组和列表的区别。上文有详细的分析。
4、SortedSet和SortedMap
二者都提供了排序的功能。 来看一个小例子:
[java] view plain copy
1. public static void main(String[] args) {
2.
3. SortedMap<String, Integer> map = new TreeMap<String, Integer>();
4. map.put("zgg", 1);
5. map.put("erqing", 3);
6. map.put("niu", 0);
7. map.put("abc", 2);
8. map.put("aaa", 5);
9.
10. Set<String> keySet = map.keySet();
11. for (String string : keySet) {
12. System.out.print(map.get(string)+" ");
13. }
14. }
输出:5 2 3 0 1
从结果看得出:SortedMap具有自动排序功能
5、TreeMap和HashMap
HashMap具有较高的速度(查询),TreeMap则提供了按照键进行排序的功能。
6、HashSet和LinkedHashSet
HashSet,为快速查找而设计的Set。存入HashSet的对象必须实现hashCode()和equals()。
LinkedHashSet,具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序),于是在使用迭代器遍历Set时,结果会按元素插入的次序显示。
7、TreeSet和HashSet
TreeSet: 提供排序功能的Set,底层为树结构 。相比较HashSet其查询速度低,如果只是进行元素的查询,我们一般使用HashSet。
8、ArrayList和Vector
同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的。
数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半
9、Collection和Collections
Collection是一系列单值集合类的父接口,提供了基本的一些方法,而Collections则是一系列算法的集合。里面的属性和方法基本都是static的,也就是说我们不需要实例化,直接可以使用类名来调用。
Java IO主要主要在java.io包下,分为四大块近80个类:
1、基于字节操作的I/O接口:InputStream和OutputStream
2、基于字符操作的I/O接口:Writer和Reader
3、基于磁盘操作的I/O接口:File
4、基于网络操作的I/O接口:Socket(不在java.io包下)
1. 定义一个接口,该接口里有需要实现的方法,并且编写实际的实现类。
2. 定义一个InvocationHandler类,实现InvocationHandler接口,重写invoke()方法,且添加getProxy()方法。
总结一下动态代理实现过程:
1. 通过getProxyClass0()生成代理类。
2. 通过Proxy.newProxyInstance()生成代理类的实例对象,创建对象时传入InvocationHandler类型的实例。
3. 调用新实例的方法,即此例中的add(),即原InvocationHandler类中的invoke()方法。
UserService.java, 只有一个方法add()。
[java] view plain copy
1. package com.adam.java.basic;
2.
3. public interface UserService {
4. public abstract void add();
5. }
建一个该接口的实现类UserServiceImpl.java
[java] view plain copy
1. package com.adam.java.basic;
2. public class UserServiceImpl implements UserService {
3.
4. @Override
5. public void add() {
6. System.out.println("----- add -----");
7. }
8. }
建一个代理处理类MyInvocationHandler.java
[java] view plain copy
1. package com.adam.java.basic;
2. import java.lang.reflect.InvocationHandler;
3. import java.lang.reflect.Method;
4. import java.lang.reflect.Proxy;
5.
6. public class MyInvocationHandler implements InvocationHandler {
7.
8. private Object target;
9.
10. public MyInvocationHandler(Object target) {
11. super();
12. this.target = target;
13. }
14.
15. public Object getProxy() {
16. return Proxy.newProxyInstance(Thread.currentThread()
17. .getContextClassLoader(), target.getClass().getInterfaces(),
18. this);
19. }
20.
21. @Override
22. public Object invoke(Object proxy, Method method, Object[] args)
23. throws Throwable {
24. System.out.println("----- before -----");
25. Object result = method.invoke(target, args);
26. System.out.println("----- after -----");
27. return result;
28. }
29.}
测试类
1. package com.adam.java.basic;
2. public class DynamicProxyTest {
3.
4. public static void main(String[] args) {
5. UserService userService = new UserServiceImpl();
6. MyInvocationHandler invocationHandler = new MyInvocationHandler(
7. userService);
8.
9. UserService proxy = (UserService) invocationHandler.getProxy();
10. proxy.add();
11. }
12.}
JDK本身有实现动态代理技术,但是略有限制,即被代理的类必须实现某个接口,否则无法使用JDK自带的动态代理,因此,如果不满足条件,就只能使用另一种更加灵活,功能更加强大的动态代理技术—— CGLIB.
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
示例
业务类:
1. package net.battier.dao;
2.
3. public interface BookFacade {
4. public void addBook();
5. }
1. package net.battier.dao.impl;
2.
3. /**
4. * 这个是没有实现接口的实现类
5. *
6. * @author student
7. *
8. */
9. public class BookFacadeImpl1 {
10. public void addBook() {
11. System.out.println("增加图书的普通方法...");
12. }
13. }
代理:
1. package net.battier.proxy;
2.
3. import java.lang.reflect.Method;
4.
5. import net.sf.cglib.proxy.Enhancer;
6. import net.sf.cglib.proxy.MethodInterceptor;
7. import net.sf.cglib.proxy.MethodProxy;
8.
9. /**
10. * 使用cglib动态代理
11. *
12. * @author student
13. *
14. */
15. public class BookFacadeCglib implements MethodInterceptor {
16. private Object target;
17.
18. /**
19. * 创建代理对象
20. *
21. * @param target
22. * @return
23. */
24. public Object getInstance(Object target) {
25. this.target = target;
26. Enhancer enhancer = new Enhancer();
27. enhancer.setSuperclass(this.target.getClass());
28. // 回调方法
29. enhancer.setCallback(this);
30. // 创建代理对象
31. return enhancer.create();
32. }
33.
34. @Override
35. // 回调方法
36. public Object intercept(Object obj, Method method, Object[] args,
37. MethodProxy proxy) throws Throwable {
38. System.out.println("事物开始");
39. proxy.invokeSuper(obj, args);
40. System.out.println("事物结束");
41. return null;
42.
43.
44. }
45.
46. }
测试;
1. package net.battier.test;
2.
3. import net.battier.dao.impl.BookFacadeImpl1;
4. import net.battier.proxy.BookFacadeCglib;
5.
6. public class TestCglib {
7.
8. public static void main(String[] args) {
9. BookFacadeCglib cglib=new BookFacadeCglib();
10. BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
11. bookCglib.addBook();
12. }
13. }
简述synchronized和java.util.concurrent.locks.Lock的异同 ?
主要相同点:Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。Lock还有更强大的功能,例如,它的tryLock方法可以非阻塞方式去拿锁。
线程安全问题,一般情况下,如果一个对象的状态是可变的,同时它又是共享的(即至少可被多于一个线程同时访问),则它存在线程安全问题。
原子性意味着一个线程一次只能执行由一个指定监控对象(lock)保护的代码,从而防止多个线程在更新共享状态时相互冲突。可见性则更为微妙;它要对付内存缓存和编译器优化的各种反常行为。一般来说,线程以某种不必让其他线程立即可以看到的方式(不管这些线程在寄存器中、在处理器特定的缓存中,还是通过指令重排或者其他编译器优化),不受缓存变量值的约束。
原子变量类基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入。
synchronized关键字是基于阻塞的锁机制,也就是说当一个线程拥有锁的时候,访问同一资源的其它线程需要等待,直到该线程释放锁,这里会有些问题:首先,如果被阻塞的线程优先级很高很重要怎么办?其次,如果获得锁的线程一直不释放锁怎么办?(这种情况是非常糟糕的)。还有一种情况,如果有大量的线程来竞争资源,那CPU将会花费大量的时间和资源来处理这些竞争(事实上CPU的主要工作并非这些),同时,还有可能出现一些例如死锁之类的情况。
Compare and swap(CAS)
每一个CAS操作过程都包含三个运算符:一个内存地址V,一个期望的值A和一个新值B,操作的时候如果这个地址上存放的值等于这个期望的值A,则将地址上的值赋为新值B,否则不做任何操作。CAS的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新值,否则不做任何事儿,但是要返回原值是多少。
CAS (compare and swap) + volatile和native方法
数据结构包括两大类:线性结构和非线性结构,线性结构包括:数组、链表、队列、栈等,非线性结构包括树、图、表等及衍生类结构。
Collections位集合类提供线程安全的支持
对于有些非线程安全的集合类,如HashMap,我们可以通过Collections的一些方法,使得HashMap变为线程安全的类,如:Collections.synchronizedMap(newHashMap());
excutor框架
Java中excutor只是一个接口,但它为一个强大的同步框架做好了基础,其实现可以用于异步任务执行,支持很多不同类型的任务执行策略。excutor框架适用于生产者-消费者模式,是一个非常成熟的框架