Zebra2
复习
- 进程和线程
- 进程是程序加载到内存中被cpu计算的过程,进程是资源分配和任务调度的最小单位,引入进程的目的--减少响应时间,提高cpu的利用率。进程的状态:就绪,运行,阻塞。线程本质上是一个简化版的进程,线程是任务执行的最小单位。
- Bio和Nio
- BIO是一个阻塞式的IO
- NIO--基于缓冲区和通道,是非阻塞式的IO---允许数据的双向传输,减少流对象的创建,保证有效的事件交给服务器处理,能够定向的操作特定的数据
- ByteBuffer
- 字节缓冲区,底层实际上是一个字节数组--容量位(缓冲区的大小),操作位(用于标识要操作的位置,默认在第0位),限制位(用于限制能达到的最大的位置,默认和容量位是一致的)--反转缓冲区(先将限制位挪到操作位的位置,再将操作位归零)
- Channel
- 通道,具有双向性,默认为阻塞的,需要手动设置为非阻塞
- Selector
- 选择器,对应的通道必须注册到对应的选择器身上,并且得到对应事件的权限,后边的选择器才会对应管理选择对应的事件
concurrent包
- 详细介绍请参考http://blog.csdn.net/defonds/article/details/44021605
- 原文参考:http://tutorials.jenkov.com/java-util-concurrent/index.html
- 阻塞式队列
- BlockingQueue
- FIFO
- 接口,主要学习实现类--ArrayBlockingQueue和LinkedBlockingQueue
基于生产者和消费者的例子
主函数
public static void main(String[] args) throws Exception { BlockingQueue queue = new ArrayBlockingQueue(1024); Producer producer = new Producer(queue); Consumer consumer = new Consumer(queue); new Thread(producer).start(); new Thread(consumer).start(); Thread.sleep(4000); }
生产者
public class Producer implements Runnable{ protected BlockingQueue queue = null; public Producer(BlockingQueue queue) { this.queue = queue; } public void run() { try { queue.put("1"); Thread.sleep(1000); queue.put("2"); Thread.sleep(1000); queue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } } }
消费者
public class Consumer implements Runnable{ protected BlockingQueue queue = null; public Consumer(BlockingQueue queue) { this.queue = queue; } public void run() { try { System.out.println(queue.take()); System.out.println(queue.take()); System.out.println(queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } } - ArrayBlockingQueue
添加元素
package com.peng.queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import org.junit.Test; public class BlockingQueueDemo { @Test public void test1() { // 创建 BlockingQueue<String> queue = new ArrayBlockingQueue<>(5); // 添加元素 queue.add("1"); queue.add("2"); queue.add("3"); queue.add("4"); queue.add("5"); System.err.println(queue); } }
- 当元素满了以后,再次添加
- add--异常(full queue)--底层实际调用了offer方法
- offer--添加成功返回true,添加失败返回false;另一种格式是当在指定时间(元素,时间数量,时间范围)
- put--阻塞,直到队列有空的地方
- 不同函数的处理方式
抛异常 | 特定值 | 阻塞 | 超时 | |
---|---|---|---|---|
增加 | add | offer,正常添加返回true,添加事变返回false | put | offer(o, timeout, timeunit) |
删除 | 队列为空时,remove添加参数正常,不加参数时抛出异常 | 对列为空时,poll,返回null | take | poll(timeout, timeunit)在对应的时间尝试删除元素, |
检查 | element | peek |
- 注意
- 抛异常:如果试图的操作无法立即执行,抛一个异常。
- 特定值:如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。
- 阻塞:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
- 超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。
LinkedBlockingQueue
- 底层基于节点的
- LinkedBlockingQueue 内部以一个链式结构(链接节点)对其元素进行存储。如果需要的话,这一链式结构可以选择一个上限。如果没有定义上限,将使用 Integer.MAX_VALUE 作为上限。
PriorityBlockingQueue
- PriorityBlockingQueue 是一个无界的并发队列。它使用了和类 java.util.PriorityQueue 一样的排序规则。你无法向这个队列中插入 null 值。 所有插入到 PriorityBlockingQueue 的元素必须实现 java.lang.Comparable 接口。因此该队列中元素的排序就取决于你自己的 Comparable 实现。
ConcurrentMap
- 接口:想使用的话就得使用它的实现类
- java.util.concurrent.ConcurrentMap 接口表示了一个能够对别人的访问(插入和提取)进行并发处理的 java.util.Map。ConcurrentMap 除了从其父接口 java.util.Map 继承来的方法之外还有一些额外的原子性方法。
- 子类ConcurrentHashMap
- ConcurrentHashMap 和 java.util.HashTable 类很相似,但 ConcurrentHashMap 能够提供比 HashTable 更好的并发性能。在你从中读取对象的时候 ConcurrentHashMap 并不会把整个 Map 锁住。此外,在你向其中写入对象的时候,ConcurrentHashMap 也不会锁住整个 Map。它的内部只是把 Map 中正在被写入的部分进行锁定。另外一个不同点是,在被遍历的时候,即使是 ConcurrentHashMap 被改动,它也不会抛 ConcurrentModificationException。尽管 Iterator 的设计不是为多个线程的同时使用。
- 例子
ConcurrentMap concurrentMap = new ConcurrentHashMap(); concurrentMap.put("key", "value"); Object value = concurrentMap.get("key");
ConcurrentNavigableMap并发导航映射
- java.util.concurrent.ConcurrentNavigableMap 是一个支持并发访问的 java.util.NavigableMap,它还能让它的子 map 具备并发访问的能力。所谓的 "子 map" 指的是诸如 headMap(),subMap(),tailMap() 之类的方法返回的 map。
- 方法
- headMap(T toKey) 方法返回一个包含了小于给定 toKey 的 key 的子 map
- tailMap(T fromKey) 方法返回一个包含了不小于给定 fromKey 的 key 的子 map。
- subMap() 方法返回原始 map 中,键介于 from(包含) 和 to (不包含) 之间的子 map
CountDownLatch
- 闭锁/线程递减锁
例子
package com.peng.lock; import java.util.concurrent.CountDownLatch; public class CountDownLatchDemo { public static void main(String[] args) throws Exception { CountDownLatch cdl = new CountDownLatch(2); Thread t1 = new Thread(new Teacher(cdl)); Thread t2 = new Thread(new Student(cdl)); t1.start(); t2.start(); cdl.await();// 判断递减是否完成了,减为了0? System.out.println("可以了,能上课了!"); } } class Teacher implements Runnable { private CountDownLatch cdl = null; public Teacher(CountDownLatch cdl) { super(); this.cdl = cdl; } @Override public void run() { System.out.println("老师走进了教室"); cdl.countDown();// 向下减数 } } class Student implements Runnable { private CountDownLatch cdl = null; public Student(CountDownLatch cdl) { super(); this.cdl = cdl; } @Override public void run() { System.out.println("学生走进了教室"); cdl.countDown();// 向下减数 } }
CyclicBarrier
- 栅栏
- 适合同一个线程类产生的多个线程
例子
package com.peng.lock; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class Tjdemo { public static void main(String[] args) { // 栅栏--等要等待的线程都到齐了才放行 CyclicBarrier cb = new CyclicBarrier(4); new Thread(new Horse(cb), "黑马").start(); new Thread(new Horse(cb), "白马").start(); new Thread(new Horse(cb), "红马").start(); new Thread(new Horse(cb), "绿马").start(); } } class Horse implements Runnable { private CyclicBarrier cb = null; public Horse(CyclicBarrier cb) { super(); this.cb = cb; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "走到了起点,准备赛马"); try { cb.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("听到声音," + Thread.currentThread().getName() + "跑了 出去~~"); } }
Exchange
- 交换机
- 交换两个线程的信息
例子
package com.peng.lock; import java.util.concurrent.Exchanger; public class ExchangeDemo { public static void main(String[] args) { Exchanger<String> ex = new Exchanger<String>(); new Thread(new Spy1(ex)).start(); new Thread(new Spy2(ex)).start(); } } // 间谍1--交换信息 class Spy1 implements Runnable { private Exchanger<String> ex = null; public Spy1(Exchanger<String> ex) { this.ex = ex; } @Override public void run() { String s1 = "地瓜,地瓜,我是土豆!"; try { String sp2_info = ex.exchange(s1); System.out.println("间谍2传给1:" + sp2_info); } catch (InterruptedException e) { e.printStackTrace(); } } } // 间谍2--交换信息 class Spy2 implements Runnable { private Exchanger<String> ex = null; public Spy2(Exchanger<String> ex) { this.ex = ex; } @Override public void run() { String s2 = "土豆土豆,我是地瓜!"; try { String sp1_info = ex.exchange(s2); System.out.println("间谍1传给2:" + sp1_info); } catch (InterruptedException e) { e.printStackTrace(); } } }
Semaphore
- 信号量
- 保护一个重要(代码)部分防止一次超过N个线程进入
- 在两个线程之间发送信号
- 限制一段时间内在某个时间段内最多只能有n个线程进入访问,每个线程的进入的时候,先进行acquire操作,信号量减少一个,减到0阻塞;release释放信号量,信号量的个数增加
例子
package com.peng.lock; import java.util.concurrent.Semaphore; public class SemphoreDemo { public static void main(String[] args) { // 信号量 Semaphore se = new Semaphore(5); for (int i = 0; i < 10; i++) { new Thread(new WhatFilm(se)).start(); } } } class WhatFilm implements Runnable { private Semaphore se = null; public WhatFilm(Semaphore se) { this.se = se; } @Override public void run() { String name = Thread.currentThread().getName(); try { se.acquire();// 把信号量减少一个 System.out.println(name + "取了一副眼镜,进场看电影!"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name + "归还了眼镜"); se.release();// 释放信号量 } }
ExecutorService
- 先交给核心线程处理,如果核心线程已经用完,再来的请求放入工作队列中,
例子
package com.peng.lock; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ExecutorServiceDemo { public static void main(String[] args) { // corePoolSize--核心池的大小--一旦创建,不再销毁 // maximumPoolSize--允许存在的最大线程数量(临时线程) // keepAliveTime--线程存活的时间 // unit--时间单位 // workQueue--工作队列,阻塞式队列 // handler-- // 核心池的大小 ExecutorService es = new ThreadPoolExecutor(5, 10, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5)); for (int i = 0; i < 15; i++) { es.submit(new EDemo()); } es.shutdown(); } } class EDemo implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "正在处理中!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
newCachedThreadPool和newFixedThreadPool
例子
package com.peng.lock; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class PoolDemo { public static void main(String[] args) { // 缓存线程池 // --核心线程池的数量为0 // --临时线程存活的时间比短(一分钟)【好处:能够很好的应对高并发的场景】【缺点:不适合长任务的场景】 // --只有一个线程 // --小队列大池子的线程池 ExecutorService es1 = Executors.newCachedThreadPool(); // 大队列小池子的线程池 // 没有临时线程,都是核心线程【优点:适合长任务的场景】【缺点:不适合短任务的场景】 // 降低服务器的并发压 ExecutorService es2 = Executors.newFixedThreadPool(5); Future<String> result = es2.submit(new Cdemo()); es2.shutdown(); } } class Cdemo implements Callable<String> { @Override public String call() throws Exception { for (int i = 0; i < 10; i++) { System.out.println("lala--" + i); } return "success"; } }
执行线程函数的区别
- submit
- 有返回值
- 可以执行的线程
- Callable
- Runnable
- execute
- 没有返回值
- 可以执行的线程
- Runnable
锁
Lock
- 和Syschronized(非公平)机制类似,但是比Syschronized更加灵活
- 策略
- 公平策略:每个线程都有机会执行到
- 非公平策略
ReentrantLock
- 默认是一个非公平的锁
- 可以设置为公平锁
- 上锁-lock
- 解锁-ulock
- 可以一个方法加锁,另一个方法解锁---可以跨方法来用锁
Lock和synchronized的区别
- 锁的时间
- 同步块不能用
- lock可以
- 在不同的方法里调用
- 同步块不可以
- lock可以
- synchronized代码块不能够保证进入访问等待的线程的先后顺序
- 不能传递任何参数给一个synchronized代码块的入口,因此,对于synchronized代码块的访问等待设置超时时间是不可能的事情
Lock方法
- lock()
- lock将Lock实例锁定,如果该Lock实例已被锁定,调用lock()方法的线程将会阻塞,直到Lock实例解锁
- lockInterruptibly()
- 方法将会被调用的线程锁定,除非该线程被打断,此外,如果一个线程在通过这个方法来锁定Lock对象时进入阻塞等待,而它被打断了的话,该线程将会退出这个方法的调用
- tryLock()
- 该方法试图立即锁定Lock实例,如果锁定成功,它将返回true,如果Lock实例已经锁定 该方法返回false,这一方法永不阻塞
- tryLock(Long timeout,TimeUnit timeUnit)
- 工作类似于tryLock方法,除了它在放弃lock之前等待一个给定的时间之外
- unlock
- 该方法对Lock实例解锁,一个Lock实现将只允许锁定了该对象的线程来调用此方法,其他(没有锁定该lock 对象的线程)线程对unlock方法的调用则会抛出一个未检查异常(RuntimeException)
ReadWriteLock
- 是一种比较先进的锁
- 同一时刻允许多个线程来读取同一个资源,但是只能一个线程来写操作
- 在读期间,不允许进行写操作;在写期间,不允许读
原子操作
- 数据操作只允许一个线程来操作
- eg:PAtomicInteger 类为我们提供了一个可以进行原子性读和写操作的 int 变量,它还包含一系列先进的原子性操作,比如 compareAndSet()。AtomicInteger 类位于 java.util.concurrent.atomic 包,因此其完整类名为 java.util.concurrent.atomic.AtomicInteger。本小节描述的
例子
package com.peng.lock; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; public class YzDemo { public static int count = 0; // 原子操作 public static AtomicInteger ai = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { CountDownLatch cd = new CountDownLatch(2); new Thread(new Ademo(cd)).start(); new Thread(new Ademo(cd)).start(); cd.await(); System.out.println(ai); } } class Ademo implements Runnable { private CountDownLatch cd = null; public Ademo(CountDownLatch cd) { this.cd = cd; } @Override public void run() { for (int i = 1; i <= 1000; i++) { YzDemo.ai.incrementAndGet(); } cd.countDown(); } }