1、JUC java.util.concurrent 并发编程工具类
- java.util.concurrent
- java.util.concurrent.atomic
- java.util.concurrent.locks
1.1 、进程及线程
进程:操作系统中运行的每一个程序就是一个进程。
线程:是进程中的一条执行路径,一个进程中可以有多个线程。
1.2 、并发及并行
并发:多线程同一时间点访问同一个资源。
并行:同时进行多个事件,泡着脚打王者荣耀,美滋滋。。。
1.3 、高内聚低耦合
高内聚:系统的架构的完整尽可能少的依赖外部资源,单个系统的维护、运行等尽可能低的影响、依赖于外部其它系统。
低耦合:系统内部各个模块间的依赖关系尽可能的低,各个模块的维护的影响的范围小,整个系统的灵活性提高(系统的改变,即各个模块的增、删、改容易),降低系统的维护成本,能尽可能的提高系统的多功能性。系统变更成本低,具备实现多功能的条件。
一个好的系统应该是分成各个小块,分而治之的思想,把复杂问题分成小问题,逐个击破。每个小块应该是高内聚,小块之间应该是低耦合的。高内聚说的是这个小块的功能已经不可分割了,已经足够简单。耦合说的是这个小块依赖其他小块提供的功能,低就是少,弱,高耦合就是多,而且强。
1.4、 多线程买票
版本一
package com.mace.juc;
/**
* 资源类
*
* @author 10836
*
*/
class Ticket {
private int ticket = 30;
public synchronized void saleTicket() {
if (ticket > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-正在售出:" + ticket-- + ",剩余:" + ticket);
}
}
}
/**
* 多线程售票
*
* @author 10836 线程 操作 资源类
*/
public class SaleTicket {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(new Runnable() {
@Override
public void run() {
for (int x = 0; x < 40; x++) {
t.saleTicket();
}
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int x = 0; x < 40; x++) {
t.saleTicket();
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int x = 0; x < 40; x++) {
t.saleTicket();
}
}
}, "C").start();
}
}
版本二
package com.mace.juc;
import java.util.concurrent.locks.ReentrantLock;
/**
* 资源类
*
* @author 10836
*
*/
class Ticket2 {
private int ticket = 30;
//可重入锁
private final ReentrantLock lock = new ReentrantLock();
public void saleTicket() {
lock.lock();//加锁
try {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "-正在售出:" + ticket-- + ",剩余:" + ticket);
}
} catch (Exception e) {
e.printStackTrace();
// TODO Auto-generated catch block
}finally {
lock.unlock();//解锁
}
}
}
/**
* 多线程售票
* 使用可重入锁及lambda表达式
* @author 10836 线程 操作 资源类
*/
public class SaleTicket2 {
public static void main(String[] args) {
Ticket2 t = new Ticket2();
//lambda表达式改写
new Thread(()->{for (int x = 0; x < 40; x++) t.saleTicket();},"A").start();
new Thread(()->{for (int x = 0; x < 40; x++) t.saleTicket();},"B").start();
new Thread(()->{for (int x = 0; x < 40; x++) t.saleTicket();},"C").start();
}
}
1.5、wait和sleep的区别
wait()方法会释放持有的锁,不然其他线程不能进入同步方法或同步块,从而不能调用notify(),notifyAll()方法来唤醒线程,产生死锁,所以释放锁,可以执行其他线程,也可以唤醒自己,只是设置停止自己的时间时不确定的;sleep方法不会释放持有的锁,设置sleep的时间是确定的会按时执行的;
sleep()必须指定时间,wait()可以指定时间也可以不指定;sleep()时间到,线程处于临时阻塞或运行状态;
1.6、线程的状态
NEW
实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。
RUNNABLE
实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。
调用线程的start()方法,此线程进入就绪状态。当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。锁池里的线程拿到对象锁后,进入就绪状态。
BLOCKED
阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
WAITING
处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
TIMED_WAITING
处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
TERMINATED
当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
1.7、 lambda表达式
- 接口中只有一个方法的声明(函数式接口@FunctionalInterface)
- 拷贝小括号,写死右箭头,落地大括号。
- 函数式接口中可以有多个default默认方法
- 函数式接口中可以有多个static静态方法
package com.mace.lamdba;
import java.util.UUID;
/**
* lambda表达式
* @author 10836
*
*/
@FunctionalInterface
interface Say{
void sayGood(String name);
default int count(int x,int y) {return x+y;}
static String uuid() {
return UUID.randomUUID().toString();
}
}
public class FirstLamdba {
public static void main(String[] args) {
Say s = (name)->{System.out.println(name +" say GOOD");};
s.sayGood("bob");
System.out.println(s.count(1, 2));
System.out.println(Say.uuid());
}
}
面向接口编程:
接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。体现了自然界“如果你是……则必须能……”的理念。接口是在一定粒度视图上同类事物的抽象表示。注意这里我强调了在一定粒度视图上,因为“同类事物”这个概念是相对的,它因为粒度视图不同而不同。在系统分析和架构中,分清层次和依赖关系,每个层次不是直接向其上层提供服务(即不是直接实例化在上层中),而是通过定义一组接口,仅向上层暴露其接口功能,上层对于下层仅仅是接口依赖,而不依赖具体类。“面向接口编程”中的接口是一种思想层面的用于实现多态性、提高软件灵活性和可维护性的架构部件,而具体语言中的“接口”是将这种思想中的部件具体实施到代码里的手段。
抽象类和接口的区别:
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果。
7、抽象类里可以没有抽象方法。
8、如果一个类里有抽象方法,那么这个类只能是抽象类。
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
1.8、 线程间的通信
生产者消费者基础版
package com.mace.juc;
/**
* 线程通信测试
* @author 10836
* 有两个线程,操作初始值为0的一个变量
* 一个线程对该变量+1,一个线程对该变量-1,
* 实现交替10轮,变量值仍为0;
*
* 高内聚低耦合 线程操作资源类
*/
class AirCon{
private int num = 0 ;
//增加
public synchronized void incr() throws InterruptedException {
if(num != 0 ) {
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"-生产\t"+num);
this.notifyAll();
}
//减少
public synchronized void decr() throws InterruptedException {
if(num ==0) {
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"-消费\t"+num);
this.notifyAll();
}
}
public class ThreadNotfiyWait {
public static void main(String[] args) {
AirCon air = new AirCon();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.incr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.decr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"B").start();
}
}
线程通信防止虚假唤醒
package com.mace.juc;
/**
* 线程通信测试
* @author 10836
* 有两个线程,操作初始值为0的一个变量
* 一个线程对该变量+1,一个线程对该变量-1,
* 实现交替10轮,变量值仍为0;
*
* 高内聚低耦合 线程操作资源类
* 判断、通知、干活
* 多线程交互中,必须防止多线程的虚假唤醒,也即(判断只能用while不能用if)
*
*/
class AirCon{
private int num = 0 ;
//增加
public synchronized void incr() throws InterruptedException {
while(num != 0 ) {
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"-生产\t"+num);
this.notifyAll();
}
//减少
public synchronized void decr() throws InterruptedException {
while(num ==0) {
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"-消费\t"+num);
this.notifyAll();
}
}
public class ThreadNotfiyWait {
public static void main(String[] args) {
AirCon air = new AirCon();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.incr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.incr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.decr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.decr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"D").start();
}
}
线程通信新写法:
package com.mace.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程通信测试-新写法
* @author 10836
* 有两个线程,操作初始值为0的一个变量
* 一个线程对该变量+1,一个线程对该变量-1,
* 实现交替10轮,变量值仍为0;
*
* 高内聚低耦合 线程操作资源类
* 判断、通知、干活
* 多线程交互中,必须防止多线程的虚假唤醒,也即(判断只能用while不能用if)
*
*/
class AirCon2{
private int num = 0 ;
private final ReentrantLock lock = new ReentrantLock();
private final Condition con = lock.newCondition();
//增加
public void incr() throws InterruptedException {
lock.lock();
try {
while(num != 0 ) {//if
con.await();//this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"-生产\t"+num);
con.signalAll();//this.notifyAll();
} finally {
lock.unlock();
}
}
//减少
public void decr() throws InterruptedException {
lock.lock();
try {
while(num ==0) {//if
con.await();//this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"-消费\t"+num);
con.signalAll();//this.notifyAll();
} finally {
lock.unlock();
}
}
}
public class ThreadNotfiyWait2 {
public static void main(String[] args) {
AirCon2 air = new AirCon2();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.incr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.incr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.decr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for(int i = 0 ; i < 10 ;i++) {
try {
air.decr();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"D").start();
}
}
线程通信精确唤醒:
package com.mace.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class PrintThread{
private int flag = 1;//1=A 2=B 3=C
private final Lock lock = new ReentrantLock();
//一道门 三把锁
private final Condition con1 = lock.newCondition();
private final Condition con2 = lock.newCondition();
private final Condition con3 = lock.newCondition();
public void print5() {
lock.lock();
try {
while(flag != 1) {
try {
con1.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int i = 1 ; i <= 5;i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
flag = 2;
con2.signal();
} finally{
lock.unlock();
}
}
public void print10() {
lock.lock();
try {
while(flag != 2) {
try {
con2.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int i = 1 ; i <= 10;i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
flag = 3;
con3.signal();
} finally{
lock.unlock();
}
}
public void print15() {
lock.lock();
try {
while(flag != 3) {
try {
con3.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int i = 1 ; i <= 15;i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
flag = 1;
con1.signal();
} finally{
lock.unlock();
}
}
}
/**
* 线程间通信精确通知
* @author 10836
* 多线程之间按顺序调用,实现A->B->C->三个线程启动:
*
* A 打印5次
* B 打印10次
* C 打印15次
* 这样打印10轮。
*
* 高内聚低耦合 线程、操作、资源类
* 判断、通知、干活
* 多线程交互中,必须防止多线程的虚假唤醒,也即(判断只能用while不能用if)
* 标志位
*/
public class ThreadOrderAccess {
public static void main(String[] args) {
PrintThread pt = new PrintThread();
new Thread(()->{
for(int i = 0 ; i < 10 ; i++) {
pt.print5();
}
},"A").start();
new Thread(()->{
for(int i = 0 ; i < 10 ; i++) {
pt.print10();
}
},"B").start();
new Thread(()->{
for(int i = 0 ; i < 10 ; i++) {
pt.print15();
}
},"C").start();
}
}
2、锁
synchronized 实现同步:
- 普通方法锁是当前实例对象
- 静态方法锁是类的Class对象
- 同步代码块是当前实例对象
package com.mace.juc;
import java.util.concurrent.TimeUnit;
class Phone{
public synchronized void sendEmail() {
System.out.println("send email。。。。");
}
public synchronized void sendSMS() {
System.out.println("send SMS。。。。");
}
}
/**
* lock机制
* @author 10836
*
*/
public class Lock {
public static void main(String[] args) throws InterruptedException {
Phone p = new Phone();
new Thread(()->{
p.sendEmail();
},"A").start();
TimeUnit.MILLISECONDS.sleep(100);
new Thread(()->{
p.sendSMS();
},"A").start();
}
}
3、list不安全
CopyOnWrite写时复制技术,是一种读写分离的思想,读写不同的容器。
package com.mace.collect;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* ArrayList 是线程不安全的
* @author 10836
* java.util.ConcurrentModificationException并发修改异常
* 导致原因:
* 解决方案:
* 1.Vector线程安全
* 2.Collections.synchronizedList(new ArrayList<>())
* 3.new CopyOnWriteArrayList<>()
* 优化建议:使用线程安全容器
*/
public class ThreadList {
public static void main(String[] args) throws Exception {
//List<String> list = new ArrayList<>();
//List<String> list = new Vector<>();
//List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList<String>();
// CopyOnWriteArrayList源码
// public boolean add(E e) {
// final ReentrantLock lock = this.lock;
// lock.lock();
// try {
// Object[] elements = getArray();
// int len = elements.length;
// Object[] newElements = Arrays.copyOf(elements, len + 1);复制
// newElements[len] = e;
// setArray(newElements);
// return true;
// } finally {
// lock.unlock();
// }
// }
for(int i = 0 ; i < 30;i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
},String.valueOf(i)).start();
}
TimeUnit.SECONDS.sleep(5);
System.out.println(list);
}
}
4、set不安全
package com.mace.collect;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class ThreadSet {
public static void main(String[] args) {
//Set<String> set =new HashSet<>();线程不安全
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<String>();
for(int i = 0 ; i < 30;i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
//实际上是HashMap
public HashSet() {
map = new HashMap<>();
}
//添加的值存储在HashMap的key,HashMap的value为new Object()
public boolean add(E e) {
return map.put(e, PRESENT)==null;
//private static final Object PRESENT = new Object();
}
5、map不安全
package com.mace.collect;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class ThreadMap {
public static void main(String[] args) {
//HashMap<String,String> map = new HashMap<>();
//Map<Object, Object> map = Collections.synchronizedMap(new HashMap<Object,Object>());
Map<Object, Object> map = new ConcurrentHashMap<Object, Object>();
for(int i = 0 ; i < 30;i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
6、callable创建线程
package com.mace.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* callable创建多线程
* @author 10836
*/
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName());
return 1;
}
}
public class CallableThread {
public static void main(String[] args) throws Exception {
FutureTask<Integer> task = new FutureTask<>(new MyThread());
new Thread(task, "A").start();
System.out.println(task.get());
}
}
6.1 callable与runable的区别
- 是否有返回值
- 是否抛异常
- 落地方法call和run
6.2 callable细节
- get方法一般放在最后一行,否则将等待直到有返回值。
- 多个线程同时调用FutureTask,首次得到返回值会进行缓存,其他线程不在重新调用。
7、多线程辅助工具类
7.1 、CountDownLatch加计数器
package com.mace.thread;
import java.util.concurrent.CountDownLatch;
/**
* 主线程必须等到其他线程全部执行完再结束
* @author 10836
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
/**
* 当一个线程或多线程调用await方法时,线程会阻塞。
* 其他线程调用countDown方法会将计数器减一。
* 当计数器为0时,因await方法阻塞的线程会被唤醒,继续执行。
*/
CountDownLatch latch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t离开教室...");
latch.countDown();
},String.valueOf(i)).start();
}
latch.await();
System.out.println("班长离开教室...");
}
}
7.2、CyclicBarrier减计数器
package com.mace.thread;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* 减法计数器
* @author 10836
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
// public CyclicBarrier(int parties, Runnable barrierAction)
CyclicBarrier cb = new CyclicBarrier(7,()->{System.out.println("**召唤神龙**");});
for (int i = 1; i < 8; i++) {
final int temp = i;
new Thread(()->{
System.out.println("收集到"+temp+"龙珠...");
try {
cb.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
7.3、Semaphore信号量
package com.mace.thread;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* 信号量控制
* @author 10836
* 6辆车停3个车位上
*/
public class SemaphoreDemo {
public static void main(String[] args) {
/**
* acquire(获取)当一个线程调用acquire操作时,它要么成功的获取信号量(信号量减一)
* 要么一直等下去,直到有线程释放信号量,或超时。
* release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。
* 目的:
* 1.多个共享资源的互斥使用
* 2.并发线程数的控制
*/
Semaphore sp = new Semaphore(3);//三个车位
for (int i = 1; i < 7; i++) {
new Thread(()->{
try {
sp.acquire();//占住车位
System.out.println(Thread.currentThread().getName()+"号车,停入车位。");
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+"号车,驶离车位。");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
sp.release();//释放车位
}
},String.valueOf(i)).start();
}
}
}
7.4 、ReadWriteLock读写锁
package com.mace.thread;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyCache{
private volatile Map<String,String> map = new HashMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void set(String key,String value) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"\t开始写入:"+key);
map.put(key, value);
TimeUnit.MILLISECONDS.sleep(500);
System.out.println(Thread.currentThread().getName()+"\t写入成功");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
public void get(String key) {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"\t开始读取");
map.get(key);
TimeUnit.MILLISECONDS.sleep(500);
System.out.println(Thread.currentThread().getName()+"\t读取成功");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
}
/**
* 多个线程同时读取一个资源类没有问题,所以为了满足并发量,
* 读取共享资源可以同时进行。但是为了满足数据的一致性,写
* 共享资源时就不应该再有其他线程可以对资源进行读或写。
* 1.读-读能共存
* 2.读-写不能共存
* 3.写-写不能共存
*/
public class ReadWriteLcok {
public static void main(String[] args) {
MyCache cache = new MyCache();
for (int i = 1; i < 6; i++) {
final int temp = i;
new Thread(()->{
cache.set(String.valueOf(temp), String.valueOf(temp));
},String.valueOf(i)).start();
}
for (int i = 1; i < 6; i++) {
final int temp = i;
new Thread(()->{
cache.get(String.valueOf(temp));
},String.valueOf(i)).start();
}
}
}
8、BlockingQueue阻塞队列
必须要阻塞/不得不阻塞, FIFO (first-in-first-out) 。
- 当队列是空的,从队列中获取元素的操作会被阻塞。
- 当队列是满的,往队列中添加元素的操作会被阻塞。
阻塞队列类型
主要方法:
package com.mace.thread;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 阻塞队列
* @author 10836
*/
public class BlockQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
System.out.println(queue.add("A"));
System.out.println(queue.add("B"));
System.out.println(queue.add("C"));
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
//java.util.NoSuchElementException 超出容量后抛出异常
System.out.println(queue.remove());
}
}
9、线程池
9.1、线程池的优势
(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
9.2、使用
package com.mace.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 线程池
* @author 10836
*
*/
public class ThreadPool {
public static void main(String[] args) throws InterruptedException {
//ExecutorService pool = Executors.newFixedThreadPool(5);//固定线程数
//ExecutorService pool = Executors.newSingleThreadExecutor();//只有一个线程
ExecutorService pool = Executors.newCachedThreadPool();//只有一个线程
for(int i = 1 ; i < 11;i++) {
final Integer temp = i;
pool.execute(()->{
System.out.println(Thread.currentThread().getName()+"处理第"+temp+"顾客。。。");
});
//TimeUnit.SECONDS.sleep(1);
}
}
}
9.3、线程池参数
/* corePoolSize:线程池中的常驻核心线程数
maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于1.
keepAliveTime:多余的空闲线程的存活时间,超过corePoolSize的线程,将在keepAliveTime到达时销毁。
unit:keepAliveTime的时间单位。
workQueue:任务队列,被提交但尚未被执行的任务。
threadFactory:生产线程中工作线程的线程工厂,一般默认即可。
handler:拒绝策略,当队列满了,并且工作线程大于等于线程池的maximumPoolSize时如何拒绝请求执行的策略。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
*/
9.4、线程池工作原理
9.5、线程池的拒绝策略
new ThreadPoolExecutor.AbortPolicy():抛出异常RejectedExecutionException
new ThreadPoolExecutor.CallerRunsPolicy():将任务返回给调用者
new ThreadPoolExecutor.DiscardPolicy():丢弃无法处理的任务
new ThreadPoolExecutor.DiscardOldestPolicy():丢弃队列中等待最久的任务
9.6、线程池参数优化设计
- cpu密集型
- io密集型
10、函数式接口
package com.mace.lamdba;
import java.util.function.Consumer;
/**
* 函数式接口测试
* @author 狼牙
*/
public class FunctionInterFaceDemo {
public static void main(String[] args) {
// Consumer<String> comsumer = new Consumer<String>() {
// @Override
// public void accept(String t) {
// }
// };
//消费型接口,有输入没有返回值。
Consumer<String> comsumer = (t)->{System.out.println(t);};
comsumer.accept("JAVA");
}
}
11、stream流式编程
package com.mace.stream;
import java.util.Arrays;
import java.util.List;
/**
* stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
* 集合讲的是数据,流将的是计算。
* 特点:
* 1.自己不会存储元素
* 2.不会改变源对象,会返回一个持有结果的新stream
* 3.stream操作是延迟执行的,需要结果的时候才执行。
* 操作:
* 1.创建一个数据源
* 2.中间操作,处理数据源
* 3.终止操作,执行中间操作链,产生结果。
* @author 狼牙
*
*/
class User{
private String name;
private int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
public class StreamDemo {
public static void main(String[] args) {
User u1 = new User("a",18);
User u2 = new User("b",22);
User u3 = new User("c",29);
List<User> list = Arrays.asList(u1,u2,u3);
list.stream().map(u->{
return u.getName().toUpperCase();
}).forEach(System.out::println);
}
}
12、ForkJoin框架
package com.mace.forkjoin;
/**
* 分支合并框架
* @author 狼牙
* ForkJoinPool
* ForkJoinTask抽象类 -> RecursiveTask实现类
*/
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
/**
* 计算0-100的和
* @author 10836
*
*/
class MyTask extends RecursiveTask<Integer>{
private static final int FLAG = 10;
private int start;
private int end;
private int result;
public MyTask(int start, int end) {
super();
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
if((end-start) <= FLAG) {
for(int i = start;i<=end;i++) {
result = result + i;
}
}else {//如果小于10继续拆分
int middle = (start + end)/2;
MyTask m1 = new MyTask(start,middle);
MyTask m2 = new MyTask(middle+1,end);
m1.fork();
m2.fork();
Integer j1 = m1.join();
Integer j2 = m2.join();
result = j1+j2;
}
return result;
}
}
public class ForkJoinDemo {
public static void main(String[] args) throws Exception {
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> task = pool.submit(new MyTask(0, 100));
System.out.println(task.get());
pool.shutdown();
}
}
12.1、异步回调
package com.mace.forkjoin;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* 异步回调
* @author 10836
*/
public class CompleTableFutureDemo {
public static void main(String[] args) throws Exception {
CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
System.out.println("异步操作没有返回值...");
});
future.get();//调用get方法才会执行
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(()->{
System.out.println("异步操作,有返回值....");
//int x = 1/0;
return 666;
});
Integer integer = supplyAsync.whenComplete((t,u)->{
//当异步任务正常执行,t为任务的返回值,u为异常对象,为null。
//当异步任务出现异常,则t为null,u为异常对象。
System.out.println("**"+t);
System.out.println("--"+u);
}).exceptionally((t)->{
//当异步任务正常执行,次方法不会被调用。
//当异步任务出现异常,t为异常对象,并且有返回值。
System.out.println("++"+t);
return 999;
}).get();
System.out.println(integer);
}
}