阻塞队列是表示线程插入与移除的队列,常用的有几个实现类(
ArrayBlockingQueue,
LinkedBlockQueue
PriorityBlockingQueue
SynchronousQueue
LinkedBlockingDeque
DelayBlockingQueue)
分别是数组队列,链阻塞队列,具有优先级的阻塞队列,同步队列,双向链阻塞队列,延迟队列。
用阻塞队列将线程插入与移除队列时有几个方法。
插入:add(o); offer(o); offer(0,time,timeuint); put(o); 注:put是阻塞方法
移除:remove(); poll(); poll(time,timeuit); take(); 注:take是阻塞方法
ConcurrentHashMap
在多线程的应用场景下,hashMap的线程是不安全的,而hashtable是线程安全的,这是因为hashtable自己本身就加了锁的原因。但是由于hashtable加了锁,所以用多线程的时候它的性能就下降了许多。
这时候就引出了ConcurrentHashMap,它也是线程安全的,并且它的性能高了很多,这是因为ConcurrentHashMap引入了分段锁的概念。这个分段在ConcurrentHashMap中叫做segments,可以简单将这个分段理解为一个一个的hashtable。对于读操作ConcurrentHashMap是不会加锁的。只有在写的时候才会枷锁,并且在加锁的时候锁的仅仅是一个segments。通常情况下一个ConcurrentHashMap可分为16个segments,所以在理论上ConcurrentHashMap的性能是hashtable的16倍。
ConcurrentNavigableMap
Java的克隆:一个类要想实现java的克隆,必须实现cloneable接口,并且重写clone方法。
CountDownLatch这个类能够使一个线程等待其他线程执行完之后再执行。例如,应用程序的主线程希望在负责启动的框架线程启动所有的启动框架服务后再执行。
CountDownLatch有一个计数器,在初始化的时候给这个计数器赋值。countDown()方法每被调用一次,这个计数器的值就减一。可以通过await()方法可以阻塞这个线程,知道计数器为零,阻塞关闭。
注意:CountDown()方法最好放在finally中执行,避免线程执行出错的时候计数器无法自减从而造成等待线程一直阻塞。
Java.util.concurrent.CyclicBarrier类是一种同步机制,它能够让所有的线程实现同步。换句话说,就是在所有的线程到达这个栅栏之前,已经到达的线程都要等待,直到所有线程到达这里,所有线程才能够做事情。
CyclicBarrier跟CountDownLatch的区别:一个是等待其他线程都走完,本线程再走(CountDownLatch),另一个是当都到达某一点的时候线程再一起走(CyclicBarrier)
用于两个线程之间的信息交互,注意是两个线程,一个或者两个以上的线程在这里都不适用。
Exchanger类提供了一个exchange方法,这个方法将信息传达给另一个线程,同时接受的返回值是另一个线程传递过来的消息。
ExecutorService实际上就相当于一个线程池,用它创建对象的时候分别有五个参数
CorePoolSize:核心线程数量
MaxmuxpoolSize:最大线程数量
keepAliveTime:线程存活时间
Unit:指定时间单位
WorkQueue:阻塞队列的类型
核心线程数量建满了之后,如果再有新的线程请求过来,会将请求存放到阻塞队列中等待被处理,如果阻塞队列已经满,就创建新的线程处理(临时线程,如果临时线程闲置的时间超过线程存活时间,线程就会被销毁),临时线程+核心线程的数量不能够超过最大线程。当池子里有闲置的线程时阻塞队列中排在首位的任务就会请求这个闲置线程,按照先进先出的规则依次处理。
注意:核心线程不会被销毁,当池子中的线程以及阻塞队列都满了之后,如果再有线程过来,这时就会交给拒绝服务助手。
注意:在这五个参数中,最大线程数量,核心线程数量以及不同的阻塞队列会造成线程池的应用场景不同。
大队列小池子,应用场景:缓解高并发场景下的服务器压力。针对这种线程池,核心线程数量不建议过大。
小队列大池子,应用场景:能够快速相应用户的请求。只要有请求,就会创建一个线程来处理。但是如果请求数量很多,并且每个请求的时间都很长,会导致池子的线程数量越来越多而且不能够得到及时的销毁。会导致内存溢出。
线程池的工作原理
我们新建一个线程的时候,一般用的都是实现Runnable接口,其实实现Callable接口也可以新创建一个线程。因为它除了具备Runnable的功能之外,它还具有Runnable不具备的功能。
它比Runnable强的地方:
1.实现Callable这个接口必须要实现call方法(类似Runnable的run方法),call方法可以抛异常,而run方法不可以抛异常。
2.Run方法的返回值必须是void,而call方法的返回值可以是任意类型。并且这个返回值可以通过线程池拿到。
Future future= es.submit(new CallRunner());
System.err.println("call方法的返回值:"+future.get()); 拿到返回值并打印出来。
锁一般分为公平锁和非公平锁,公平锁的吞吐两很低,因此性能也很低。
而非公平锁的吞吐量相对公平锁较高,性能比公平锁高,因为它允许线程插队,就利用了线程等待唤醒的时间。
Sychronized是属于公平锁机制的一种,吞吐量很低,性能也很低。
重入锁ReentranLock支持公平锁和非公平锁两种机制,通过构造方法传入一个boolean值,如果是false则是非公平策略。
重入锁利用lock和unlock进行锁的保护,在锁释放的时候一定要记住在finally代码块中释放,否则如果线程出现异常的时候会导致锁永远都得不到释放。
重入锁是利用lock方法和unlock方法来进行上锁和解锁的,而Sychronized则将开锁的任务交给JVM来做。
公平锁跟非公平锁
FileChannel是文件通道。通过文件通道,可以根据指定的位置操作数据。
文件通道不能单独创建某个具体输入流输出流来创建。
例如:FileChannel fc=new FileInputStream(new File("1.txt")).getChannel();输入通道
FileChannel fc=new FileOutputStream(new File("2.txt")).getChannel();输出通道
FileChannel raf=new RandomAccessFile(new File("1.txt"),"rw").getChannel();这个通道既可以进行读操作,又可以进行写操作。重点掌握
通道有几个方法:position(int),通过这个方法可以起到定位的作用
通道在读数据或者写数据的时候都是用ByteBuffer来操作的。