JUC详解
JUC环境
1. 什么是JUC
是一套jdk1.5提供的一套常用于高并发和保证线程安全的包
java.util.concurrent-----高并发
java.util.concurrent.lock线程锁
java.util.concurrent.atomic原子性
特点:
JUC是java.util工具包,包是为了分类,或者避免同名
业务:普通的线程代码 Thread
Runnable没有返回值,效率比Callable低
五套接口
BlockQueue-----------------阻塞队列
ExecutorService---------执行器服务
- 连接池:访问数据库会有大量的连接池和销毁,避免对资源的浪费—减少连接和销毁的过程,做到资源的重用
- 线程池:访问服务端会产生大量的连接和销毁,避免对资源的浪费–减少连接和销毁的过程。
- 每过来一个请求,都会创建一个线程去处理这个请求,知道线程数达到指定的数量不再创建为止,这些线程称之为核心线程。
- 在核心线程数量达到
就是访问和销毁
2. 线程和进程
进程:一个程序,QQ.exe 程序的集合;
一个进程包含多个线程,至少包含一个
java默认有几个线程?2个,main和GC线程
线程:开了一个进程Typora,写字,自动保存(线程负责)
Thread、Runnable、Callable
java真的能开线程吗?不能
3. 并发和并行
并发:多线程操作同一个资源(CPU一核:模拟多线程)
本质:充分利用CPU的资源
public class JUC {
public static void main(String[] args) {
//获取CPU的核数
//CPU密集数、IO密集数
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并行:多个人一起行走(CPU多核)
1. 线程有几个状态?
public enum State {
//新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待,一直等
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
2.wait/sleep区别
1,来自不同的类
wait=>object
sleep=>Thread
2,关于锁的释放
wait会释放锁
sleep不会释放
3.使用范围不同
wait必须在同步代码块中
sleep可以在任何地方睡
4.是否需要捕获异常
wait不需要捕获异常
sleep需要捕获异常
3.Lock锁(重点)
1.传统:synchronized
/**
* @author pcy
* @date 2021/1/25 - 16:16
* 基本的卖票
*/
/**
*真正多线程开发,公司中的开发
* 线程就是一个单独的资源类,没有任何的附属的操作
* 1.属性和方法
*/
public class demo1 {
public static void main(String[] args) {
Ticket ticket=new Ticket();
//lambda编程(参数)->{代码}
new Thread(()->{
for (int i=1;i<60;i++){
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i=1;i<60;i++){
ticket.sale();
}
},"B").start();
new Thread(() -> {
for (int i=1;i<60;i++){
ticket.sale();
}
},"C").start();
}
}
//资源类oop
class Ticket{
private int number = 30;
//synchronized排队
//卖票的方式
public synchronized void sale(){
if (number > 0){
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
}
}
}
2. Lock锁
公平锁:可以先来后到(30s,3h)
非公平锁:可以插队(默认)
package com.kuangshen;
/**
* @author pcy
* @date 2021/1/25 - 16:16
* 基本的卖票
*/
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*真正多线程开发,公司中的开发
* 线程就是一个单独的资源类,没有任何的附属的操作
* 1.属性和方法
*/
public class demo2 {
public static void main(String[] args) {
Ticket ticket=new Ticket();
//lambda编程(参数)->{代码}
new Thread(()->{
for (int i=1;i<60;i++){
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i=1;i<60;i++){
ticket.sale();
}
},"B").start();
new Thread(() -> {
for (int i=1;i<60;i++){
ticket.sale();
}
},"C").start();
}
}
//资源类oop
/**
* new ReentrantLock
* 加锁
* 解锁
*/
class Ticket2{
private int number = 30;
Lock lock=new ReentrantLock();
//卖票的方式
public void sale(){
//加锁
lock.lock();
//尝试获取锁
lock.tryLock();
try {
//业务代码块
if (number > 0){
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余:" + number);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//解锁
lock.unlock();
}
}
}
3 synchronized和Lock区别
- synchronized内置的java关键字,lock是一个java类
- synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
- synchronized会自动释放锁,但是lock必须手动获取锁,如果不获取锁,就会死锁
- synchronized线程1(获得锁,阻塞)线程2(一直等待),lock就不一定会等下去
- synchronized可重入锁,不可以中断,非公平性;lock,可重入锁;可以判断锁,非公平性(非公平)可自己设置
- synchronized适合锁少量的代码同步问题,Lock适合锁大量同步代码!
4.锁是什么?如何判断锁的是谁?
八锁现象
package com;
import java.util.concurrent.TimeUnit;
/**
* @author pcy
* @date 2021/1/28 - 14:20
* 8锁就是关于锁的8个问题
* 1.标准情况下先发短信还是先打电话?发短信
*
*/
public class lock8 {
public static void main(String[] args) {
Phone phone=new Phone();
//锁的存在
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
//锁的对象是方法的调用者
//两个方法是同一个锁,谁先拿到谁执行
public synchronized void sendSms(){
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
package com;
import java.util.concurrent.TimeUnit;
/**
* @author pcy
* @date 2021/1/28 - 14:20
* 8锁就是关于锁的8个问题
* 2.一个普通的,一个锁
*
*/
public class lock8test2 {
public static void main(String[] args) {
Phone2 phone=new Phone2();
//锁的存在
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
new Thread(()->{
phone.Hello();
},"B").start();
}
}
class Phone2{
//锁的对象是方法的调用者
//两个方法是同一个锁,谁先拿到谁执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void Hello(){
System.out.println("Hello");
}
}
package com;
import java.util.concurrent.TimeUnit;
/**
* @author pcy
* @date 2021/1/28 - 14:20
* 8锁就是关于锁的8个问题
* 1.标准情况下先发短信还是先打电话?发短信
*
*/
public class lock8test3 {
public static void main(String[] args) {
//两个对象,两把锁,两个调用者
Phone3 phone=new Phone3();
Phone3 phone3=new Phone3();
//锁的存在
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone3.call();
},"B").start();
}
}
class Phone3{
//锁的对象是方法的调用者
//两个方法是同一个锁,谁先拿到谁执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void Hello(){
System.out.println("Hello");
}
}
package com;
import java.util.concurrent.TimeUnit;
/**
* @author pcy
* @date 2021/1/28 - 14:20
* 8锁就是关于锁的8个问题
* 1.标准情况下先发短信还是先打电话?发短信
* 4.增加连个静态的同步方法
*
*/
public class lock8test4 {
public static void main(String[] args) {
Phone4 phone=new Phone4();
//锁的存在
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone4{
//锁的对象是方法的调用者
//两个方法是同一个锁,谁先拿到谁执行
//类一加载就有了,锁的是class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
package com;
import java.util.concurrent.TimeUnit;
/**
* @author pcy
* @date 2021/1/28 - 14:20
* 8锁就是关于锁的8个问题
* 1.标准情况下先发短信还是先打电话?发短信
* 4.增加连个静态的同步方法
* 5.两个对象两个静态方法
*/
public class lock8test5 {
public static void main(String[] args) {
//锁的是一个class
Phone5 phone=new Phone5();
Phone5 phone5 = new Phone5();
//锁的存在
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone5.call();
},"B").start();
}
}
class Phone5{
//锁的对象是方法的调用者
//两个方法是同一个锁,谁先拿到谁执行
//类一加载就有了,锁的是class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
package com;
import java.util.concurrent.TimeUnit;
/**
* @author pcy
* @date 2021/1/28 - 14:20
* 8锁就是关于锁的8个问题
* 1.标准情况下先发短信还是先打电话?发短信
* 4.增加连个静态的同步方法
* 6.一个静态一个普通
*
*/
public class lock8test6 {
public static void main(String[] args) {
Phone6 phone=new Phone6();
//锁的存在
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone6{
//锁的对象是方法的调用者
//两个方法是同一个锁,谁先拿到谁执行
//类一加载就有了,锁的是class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
总结:
static:锁的是一个class模版
5.生产者和消费者问题
单例,排序,生产者和消费者、死锁
1.synchronized版本的生产者和消费者
package com;
/**
* @author pcy
* @date 2021/1/25 - 17:31
*/
/**
* 线程之间的通信问题:生产者和消费者问题,等待唤醒,通知唤醒
* 线程交替执行A B操作同一个变量 num=0
* A num+1
* B num-1
*/
public class cousmer {
public static void main(String[] args) {
Data data=new Data();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class Data{
//数据资源类
//判断等待业务通知
private int number=0;
public synchronized void increment() throws InterruptedException {
if (number != 0){
//等待操作
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我+1完毕
this.notify();
}
public synchronized void decrement() throws InterruptedException {
if (number == 0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notify();
}
}
问题存在:4个线程,就会出现问题(防止虚假唤醒)
解决if改为while,if语句如果一个线程进去了下一个线程进去的时候不会停下,但是while语句如果一个线程被唤醒了,那么下一个线程就会等待
package com;
/**
* @author pcy
* @date 2021/1/25 - 17:31
*/
/**
* 线程之间的通信问题:生产者和消费者问题,等待唤醒,通知唤醒
* 线程交替执行A B操作同一个变量 num=0
* A num+1
* B num-1
*/
public class cousmer {
public static void main(String[] args) {
Data data=new Data();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{
//数据资源类
//判断等待业务通知
private int number=0;
public synchronized void increment() throws InterruptedException {
while (number != 0){
//等待操作
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我+1完毕
this.notify();
}
public synchronized void decrement() throws InterruptedException {
while (number == 0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notify();
}
}
2.JUC的消费者和生产者
通过Lock找到Condition
package com.kuangshen;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author pcy
* @date 2021/1/26 - 10:25
*/
public class JUCcuster {
public static void main(String[] args) {
Data1 data=new Data1();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
data.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data1{
//数据资源类
//判断等待业务通知
private int number=0;
Lock lock=new ReentrantLock();
//同步监视器
Condition condition=lock.newCondition();
public void increment(){
lock.lock();//加锁
try {
while (number != 0){
//等待操作
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我+1完毕
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();//加锁
try {
while (number == 0){
//等待操作
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我+1完毕
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
Condition的优势:因为是老版的是随机的,而Condition可以精准的通知和唤醒操作
生产线:用户管理
下单-》支付-》交易-》物流
package com.kuangshen;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author pcy
* @date 2021/1/26 - 10:55
*/
//A执行完要用B,B执行完要用C
public class C {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for (int i=1;i<10;i++){
data3.printA();
}
},"A").start();
new Thread(()->{
for (int i=1;i<10;i++){
data3.printB();
}
},"B").start();
new Thread(()->{
for (int i=1;i<10;i++){
data3.printC();
}
},"C").start();
}
}
class Data3{
private Lock lock=new ReentrantLock() ;
//同步监视器
Condition condition1=lock.newCondition();
Condition condition2=lock.newCondition();
Condition condition3=lock.newCondition();
private int number = 1;
public void printA(){
lock.lock();
try {
while (number != 1) {
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>A");
number = 2;
condition2.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number != 2) {
//等待
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>B");
number = 3;
condition3.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (number != 3) {
//等待
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>C");
number = 1;
condition1.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
6.集合类不安全
1.List不安全
package com.Set;
import sun.security.util.ArrayUtil;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author pcy
* @date 2021/1/28 - 14:57
*/
//java.util.ConcurrentModificationException并发修改异常
public class ListTest {
public static void main(String[] args) {
//并发下ArrayList是不安全的
/**
* 解决方法是:
* 1. List<String> list = new Vector<>();Vector是安全的
* 2. List<Object> list = Collections.synchronizedList(new ArrayList<>());
* 3. List<Object> list = new CopyOnWriteArrayList<>();
*/
/**
* CopyOnWriteArrayList写入时复制 COW是计算机中程序设计时的一种优化策略
* 多个线程调用的时候,list读取的时候,固定的写入(覆盖)
* 在写入的时候避免覆盖,造成数据问题
* 读写分离
* CopyOnWriteArrayList比Vector好在哪里?
* 有synchronized时候效率比较低,而Vector调用的是synchronized
* copy则调用的是lock锁
*
*/
List<Object> list = new CopyOnWriteArrayList<>();
for (int i=1;i <= 20;i++){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
2.Set不安全
package com.Set;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @author pcy
* @date 2021/1/28 - 15:28
*/
public class SetTest {
public static void main(String[] args) {
//Set<String> set = new HashSet<>();
//Set<Object> s = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i=1;i<=10;i++){
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
}).start();
}
}
}
hashSet的底层:
public HashSet() {
map = new HashMap<>();
}
3.Map不安全
package com.Set;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author pcy
* @date 2021/1/28 - 17:08
*/
public class MapTest {
public static void main(String[] args) {
/**map是这样用的吗 Map<String,String> map=new HashMap<>();
* 不是,工作中不用
*/
//默认等价于什么?两个原始的参数 new HashMap<>(16,0.75);
//Map<String,String> map=new HashMap<>();
ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
for (int i=1;i<=10;i++){
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
}).start();
}
}
}
7,Callable
1.类似于Runnable,Runnable不会返回结果,也不会抛出异常而Callable则相反
2.方法不同
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
package com;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author pcy
* @date 2021/1/28 - 17:28
*/
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
new Thread().start();//怎么启动callable
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//会被缓存执行一次call
//得到返回结果
String s=(String)futureTask.get();//会被阻塞
//可以通过异步通信处理
System.out.println(s);
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("call()");
//如果操作耗时
return "S";
}
}
细节:
1.结果会被缓存
2.结果可能需要等待,结果会被阻塞
8.三大常用辅助类
1.CountDownLatch(减法操作)
允许一个或者多个线程等待直到在其他的线程中执行一组操作或者完成同步辅助 用来计数的
package com.add;
import java.util.concurrent.CountDownLatch;
/**
* @author pcy
* @date 2021/1/28 - 17:57
*/
//计数器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch=new CountDownLatch(6);//6个线程
for (int i=1;i<=6;i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"go");
countDownLatch.countDown();//实行减一操作
}).start();
}
countDownLatch.await();//等待计数器归零之后,再执行以后的操作
System.out.println("close door");
}
}
2.CyclicBarrier(加法操作)
package com.add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author pcy
* @date 2021/1/28 - 18:08
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
//集齐七颗龙珠
CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{
System.out.println("召唤");
});
for (int i=1;i<=7;i++){
int empt=i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"集齐"+empt+"颗龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
注意:这里不能直接使用i,因为是两个类
3.Semaphore
信号量:多个资源互斥使用
package com.add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* @author pcy
* @date 2021/1/28 - 19:54
*/
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数量:停车位3
Semaphore semaphore = new Semaphore(3);
for (int i=1;i<=6;i++) {
new Thread(()->{
try {
semaphore.acquire();//得到
//-1操作
System.out.println(Thread.currentThread().getName()+"得到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"释放车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放
}
}).start();
}
}
}
9.读写锁
ReadWriteLock:读的时候可以有多个线程去读,写的时候只能有一个线程去写
package com.rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author pcy
* @date 2021/1/28 - 20:11
*/
public class ReadWriteLockDemo {
/**
* 自定义缓存
* 独占锁:写锁,一次只能有一个线程占有
* 共享锁:读锁,
*/
public static void main(String[] args) {
Mychche mychche=new Mychche();
for (int i=1;i<=5;i++){
final int temp=i;
new Thread(()->{
mychche.put(temp+"",temp+"");
}).start();
}
for (int i=1;i<=5;i++){
final int temp=i;
new Thread(()->{
mychche.get(temp+"");
}).start();
}
}
}
class Mychche{
private volatile Map<String,Object> map =new HashMap<>();
ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
//存,写
public void put(String key,Object value){
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println("写入成功");
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}
}
//取,读
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o=map.get(key);
System.out.println(Thread.currentThread().getName()+"读取成功");
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}
}
}
10.阻塞队列
阻塞:
队列:
特点:“FIFO”:先进先出和set,list同级
什么情况下使用BlockingQueue?
多线程并发处理,线程池
如何使用队列?
添加,移除
四组API
方式 | 抛出异常 | 不抛出异常,有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer |
移除 | remove | poll | take | poll |
判断队列首 | element | peek | - | - |
package com.Bq;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* @author pcy
* @date 2021/1/28 - 20:40
* Collection
* List
* Set
* BlockingQueue
* 上面三个同级
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
/**
* 抛出异常
*/
test4();
}
//抛出异常
public static void test1(){
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
blockingQueue.element();//查看对首的名字
//IllegalStateException: Queue full
//System.out.println(blockingQueue.add("d"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//NoSuchElementException
//System.out.println(blockingQueue.remove());
}
//不抛出异常
public static void test2(){
ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue(3);
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
System.out.println(arrayBlockingQueue.offer("d"));//false不抛出异常
System.out.println(arrayBlockingQueue.peek());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());//null不抛出异常
}
//等待,阻塞
public static void test3() throws InterruptedException {
ArrayBlockingQueue blockingQueue=new ArrayBlockingQueue(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d");//等待,如果不移除则一直阻塞
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
//System.out.println(blockingQueue.take());//等待,如果不移除,则一直阻塞
}
//超时等待
public static void test4() throws InterruptedException{
ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue(3);
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
System.out.println(arrayBlockingQueue.offer("d",2, TimeUnit.SECONDS));//等待,超过2秒则退出
System.out.println(arrayBlockingQueue.peek());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));//
}
}
SynchronousQueue
同步队列:没有容量,进去一个元素,必须等待取出来之后,才能继续放进一个元素
与BlockingQueue是父子关系
package com.Bq;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* @author pcy
* @date 2021/1/29 - 10:15
* 同步队列
* 不存储元素,只要put一个元素,必须取出来
*/
public class SynchronousQueueTest {
public static void main(String[] args) {
//与ArrayBlockingQueue是父子关系
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();//同步队列
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"推进一个1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName()+"推进一个2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName()+"推进一个3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"取出一个1");
synchronousQueue.take();
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"取出一个2");
synchronousQueue.take();
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"取出一个3");
synchronousQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
}
}
11、线程池
池化技术:事先准备好一些资源,有人要用就来这里拿,用完后还回来。
默认大小:
程序运行的本质是:占用资源,资源的优化=》池化技术
线程池,内存池,对象池//创建和销毁十分得浪费资源
线程池:三大方法,七大参数,4种拒绝参数
线程池的好处:
- 降低资源的消耗
- 提高响应的速度、
- 方便管理
线程复用、可以控制最大并发数、管理线程
三大方法:
package com.Pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author pcy
* @date 2021/1/30 - 8:16
* 使用了线程池之后,要使用线程池创建线程
*/
public class Demo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();//只有单个线程
//Executors.newFixedThreadPool(3);//创建一个固定线程池的大小
//Executors.newCachedThreadPool();//可伸缩性的,遇强则强,遇弱则弱
//使用线程池来创建线程
try {
for (int i = 1; i < 10; i++) {
threadPool.execute(() -> {
System.out.println(new Thread().currentThread().getName() + "ok");
});
}
}catch (Exception e){
}finally {
//线程池用完要关闭
threadPool.shutdown();
}
}
}
七大参数:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//约等于21亿
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
本质:ThreadPoolExecutor
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;
}
手动创建一个线程池
package com.Pool;
import org.omg.SendingContext.RunTime;
import java.util.concurrent.*;
/**
* @author pcy
* @date 2021/1/30 - 8:16
* 使用了线程池之后,要使用线程池创建线程
*/
public class Demo {
public static void main(String[] args) {
ThreadPoolExecutor threadPool=new ThreadPoolExecutor(
2,
//获取cpu的核数
Runtime.getRuntime().availableProcessors(),
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
//四大拒绝策略
// new ThreadPoolExecutor.AbortPolicy()
new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试和最早的竞争,如果竞争成了,就会成功,也不会抛出异常
// new ThreadPoolExecutor.CallerRunsPolicy()//哪里来的去哪里执行也就是main方法执行
// new ThreadPoolExecutor.DiscardPolicy()//队列满了,不会抛出异常
);//银行满了,还有人进来就会报出异常
try {
//最大的报出异常max+queue
for (int i = 1; i < 10; i++) {
threadPool.execute(() -> {
System.out.println(new Thread().currentThread().getName() + "ok");
});
}
}catch (Exception e){
}finally {
//线程池用完要关闭
threadPool.shutdown();
}
}
}
最大线程池到底该如何定义?
-
cpu密集型:几核就定义为几,可以保证cpu效率最高
-
IO密集型:如果一个程序,15个大型任务,io十分占用资源
判断程序中消耗io的线程
12、四大函数式接口(必备)
新时代程序员掌握:lambda表达式、链式编程、函数式接口、Stream流式计算
函数式接口:只有一个方法的接口
作用:简化编程模型,在新版本的框架底层大量应用!
foreach(消费者函数式接口)
四大原生函数式接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//代码测试
package com.function;
import java.util.function.Function;
/**
* @author pcy
* @date 2021/1/30 - 9:24
* function:有一个输入参数,有一个输出
* 只要是函数式接口,就能用lambda表达式简化
*/
public class demo {
public static void main(String[] args) {
/* Function function=new Function<String,String>() {
@Override
public String apply(String str) {
return str;
}
//工具类:输出输入的值
};*/
Function<String,String> function=((str)->{return str;});
System.out.println(function.apply("asd"));
}
}
断定型接口:
package com.function;
import java.util.function.Predicate;
/**
* @author pcy
* @date 2021/1/30 - 9:52
* 断定型接口:有一个输入参数,返回值只能是布尔值!
*/
public class demo2 {
public static void main(String[] args) {
//判断字符串是否为空
/*Predicate predicate=new Predicate<String>() {
@Override
public boolean test(String s) {
return s.isEmpty();
}
} ;*/
Predicate<String> predicate=(str)->{return str.isEmpty();};
System.out.println(predicate.test("asd"));
}
}
3.Consumer:消费性接口
package com.function;
import java.util.function.Consumer;
/**
* @author pcy
* @date 2021/1/30 - 9:59
* 消费型接口只有输入没有返回值,输出一个数字
*
*/
public class demo3 {
public static void main(String[] args) {
/*Consumer<String> consumer=new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};*/
Consumer<String> consumer=(str)->{
System.out.println(str);
};
consumer.accept("ads");
}
}
4.Supplier:供给型接口
package com.function;
import java.util.function.Supplier;
/**
* @author pcy
* @date 2021/1/30 - 10:06
*/
public class demo4 {
public static void main(String[] args) {
/*Supplier<String> supplier=new Supplier<String>() {
@Override
public String get() {
return "ads";
}
};*/
Supplier<String> supplier=()->{
return "asd";
};
System.out.println(supplier.get());
}
}
13、Stream流式计算
什么是流计算?
大数据:存储+计算
集合、mysql:本质是存储东西的
计算应该交给流来操作
package com.Stream;
import javax.jws.soap.SOAPBinding;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author pcy
* @date 2021/1/30 - 20:59
*/
public class Test {
public static void main(String[] args) {
User user=new User(1,"a",21);
User user2=new User(2,"b",22);
User user3=new User(3,"c",23);
User user4=new User(4,"d",24);
User user5=new User(5,"e",25);
List<User> list= Arrays.asList(user, user2, user3, user4, user5);
list.stream()
//列式编程,Stream流式计算
.filter(u->{return u.getId()%2==0;})//输出偶数
.filter(u->{return u.getAge()>22;})//年龄大于22
.map(u->{return u.getName().toUpperCase();})//转化为大写
.sorted((uu1,uu2)->{return uu1.compareTo(uu2);})//用户字母倒着排序
.limit(1)//只输出一个用户
.forEach(System.out::println);
}
}
14、ForkJoin
什么是ForkJoin?
jdk1.7出来,并行执行任务,在大数据量的时候提高效率
大数据:Map Reduce(把大任务拆分成小任务)
特点:工作窃取,维护的是双端队列