博客主页:作者主页
简介:JAVA领域优质创作者、一名在校大三学生、在校期间参加各种省赛、国赛,斩获一系列荣誉。
关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿。
JUC是 java util concurrent
面试高频问JUC~!
java.util 是Java的一个工具包
业务:普通的线程代码 Thread
Runnable: 没有返回值、效率相比于Callable 相对较低!
进程:一个程序,允许一个java程序会进程里面会出现一个java.exe;数据+代码+pcb
一个进程可以包含多个线程,至少包含一个线程!
Java默认有几个线程?2个线程! main线程、GC线程
线程:开了一个进程qq,聊天打字,消息提示(线程负责的)
对于Java而言:Thread、Runable、Callable进行开启线程的。
JAVA真的可以开启线程吗? 开不了的!
原因Java没有权限去开启线程、操作硬件的,这是一个native的一个本地方法,它调用的底层的C++代码。
并发、并行
并发: 多线程操作同一个资源。
并行: 多个人并排行走。
public class Test {
public static void main(String[] args) {
//获取cpu的核数
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发编程的本质:充分利用CPU的资源!
线程的6个状态
public enum State {
//创建
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
区别 | wait | sleep |
---|---|---|
操作的类 | Object | Thread |
锁的释放 | 会释放锁 | 抱着锁睡觉 |
范围 | 同步代码块中 | 任何地方 |
异常捕获 | 不需要捕获异常 | 需要捕获异常 |
synchronized锁问题
package com.zmz.day01;
/**
* @ProjectName: Juc
* @Package: com.zmz.day01
* @ClassName: TicketTest
* @Author: 张晟睿
* @Date: 2021/9/5 14:01
* @Version: 1.0
*/
//资源类 属性 + 方法 oop
class Ticket{
private int num = 50;
//卖票方式 synchronized 本质:队列 锁
public synchronized void sale(){
if(num > 0){
System.out.println(Thread.currentThread().getName()+ " 卖出了第"+ num +" 张票,剩余:"+ --num +" 张票");
}
}
}
public class TicketTest {
public static void main(String[] args) {
//多线陈操作
//并发:多个线程操作同一个资源ticket
Ticket ticket = new Ticket();
//@FunctionalInterface 函数式接口 jdk1.8之后 lambda表达式
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"C").start();
}
}
Lock接口
公平锁: 公平,必须先来后到~;
非公平锁: 不公平,可以插队;(默认为非公平锁)
使用Lock进行操作
package com.zmz.day01;/**
* @ProjectName: Juc
* @Package: com.zmz.day01
* @ClassName: TicketTest2
* @Author: 张晟睿
* @Date: 2021/9/5 16:15
* @Version: 1.0
*/
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*@ClassName TicketTest2
*@Description
*@Author 张晟睿
*@Date 2021/9/5
**/
class Ticket2{
/*
* 加锁三步
* 1.实例化lock对象
* 2.lock加锁
* 3.unlock解锁
* */
Lock l = new ReentrantLock();
private int num = 50;
//卖票方式 synchronized 本质:队列 锁
public void sale(){
//加锁
l.lock();
try {
//业务代码
if(num > 0){
System.out.println(Thread.currentThread().getName()+ " 卖出了第"+ num +" 张票,剩余:"+ --num +" 张票");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
l.unlock();
}
}
}
public class TicketTest2 {
public static void main(String[] args) {
//多线陈操作
//并发:多个线程操作同一个资源ticket
Ticket ticket = new Ticket();
//@FunctionalInterface 函数式接口 jdk1.8之后 lambda表达式
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"C").start();
}
}
区别 | synchronized | lock |
---|---|---|
名称 | 属于关键字 | 属于对象 |
状态 | 不可以获取锁的状态 | 可以获取锁的状态 |
锁的管理 | 自动释放锁 | 需要手动加锁以及释放锁 |
线程 | 自己抱着锁 | 等待 |
可重入锁,不可以中断的,非公平的 | 可重入的,可以判断锁,可以自己设置公平锁和非公平锁 | |
代码同步 | 适合少量的代码同步 | 适合大量的代码同步 |
synchronized版
package com.zmz.day01;
/**
* @ProjectName: Juc
* @Package: com.zmz.day01
* @ClassName: TicketTest3
* @Author: 张晟睿
* @Date: 2021/9/5 16:35
* @Version: 1.0
*/
/**
*@ClassName TicketTest3
*@Description
*@Author 张晟睿
*@Date 2021/9/5
**/
public class TicketTest3 {
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;
// +1操作
public synchronized void increment() throws InterruptedException {
if(number != 0 ){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
// -1操作
public synchronized void decrement() throws InterruptedException{
if (number == 0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
}
问题存在,A线程B线程,现在如果我有四个线程A B C D!该怎么去解决问题
if判断改为While判断就可以解决虚假唤醒的问题。
package com.zmz.day01;
/**
* @ProjectName: Juc
* @Package: com.zmz.day01
* @ClassName: TicketTest3
* @Author: 张晟睿
* @Date: 2021/9/5 16:35
* @Version: 1.0
*/
/**
*@ClassName TicketTest3
*@Description
*@Author 张晟睿
*@Date 2021/9/5
**/
//线程之间的通讯问题:生产者和消费者的问题! 等待唤醒,通知唤醒
//线程交替执行 A B操作同一个资源
public class TicketTest3 {
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.decrement();
} 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;
// +1操作
public synchronized void increment() throws InterruptedException {
while(number != 0 ){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
// -1操作
public synchronized void decrement() throws InterruptedException{
while (number == 0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
}
JUC版本的解决A B C D多线程的问题
package com.zmz.day01;
/**
* @ProjectName: Juc
* @Package: com.zmz.day01
* @ClassName: JucTest1
* @Author: 张晟睿
* @Date: 2021/9/5 19:34
* @Version: 1.0
*/
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*@ClassName JucTest1
*@Description
*@Author 张晟睿
*@Date 2021/9/5
**/
public class JucTest1 {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for(int i=0;i<10;i++) {
data.increment();
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++) {
data.decrement();
}},"B").start();
new Thread(()->{
for(int i=0;i<10;i++) {
data.increment();
}
},"C").start();
new Thread(()->{
for(int i=0;i<10;i++) {
data.decrement();
}
},"D").start();
}
}
class Data2{
private int number = 0;
//lock锁
Lock l = new ReentrantLock();
Condition condition = l.newCondition();
public void increment() {
l.lock();
try {
//业务
while (number!=0){
//等待操作
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 我+1完毕了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
public void decrement() {
l.lock();
try {
//业务
while (number==0){
//等待操作
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 我-1完毕了
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
}
Condition的优势:精准通知、唤醒的线程
package com.zmz.day01;
/**
* @ProjectName: Juc
* @Package: com.zmz.day01
* @ClassName: JucTest2
* @Author: 张晟睿
* @Date: 2021/9/5 19:52
* @Version: 1.0
*/
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*@ClassName JucTest2
*@Description
*@Author 张晟睿
*@Date 2021/9/5
**/
public class JucTest2 {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for(int i=0;i<10;i++){
data3.printA();
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++){
data3.printB();
}
},"B").start();
new Thread(()->{
for(int i=0;i<10;i++){
data3.printC();
}
},"C").start();
}
}
class Data3{
private Lock l = new ReentrantLock();
Condition condition1 = l.newCondition();
Condition condition2 = l.newCondition();
Condition condition3 = l.newCondition();
private int flag = 1;
public void printA(){
l.lock();
//判断 -> 执行 -> 通知
try {
while(flag != 1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "->A" );
flag = 2;
//唤醒指定线程
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
public void printB(){
l.lock();
//判断 -> 执行 -> 通知
try {
while(flag != 2){
//等待
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "->BB" );
flag = 3;
//唤醒指定线程
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
public void printC(){
l.lock();
//判断 -> 执行 -> 通知
try {
while(flag != 3){
//等待
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "->CCC" );
flag = 1;
//唤醒指定线程
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
}
1-2锁
package com.zmz.lock8;/**
* @ProjectName: Juc
* @Package: com.zmz.lock8
* @ClassName: Test1
* @Author: 张晟睿
* @Date: 2021/9/5 21:18
* @Version: 1.0
*/
import java.util.concurrent.TimeUnit;
/**
*@ClassName Test1
*@Description
*@Author 张晟睿
*@Date 2021/9/5
**/
/*
* 8锁,就是关于锁的8个问题
* 1、标准情况下,两个线程先打印 发短信还是打电话? 发短信
* 2、sendSms方法延迟4s,两个线程先打印 发短信还是打电话? 发短信
* */
public class Test1 {
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{
//synchronized锁的对象是方法的调用者!
//两个方法用的都是phone对象的锁!
//谁先拿到谁执行!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
3-4锁
package com.zmz.lock8;/**
* @ProjectName: Juc
* @Package: com.zmz.lock8
* @ClassName: Test2
* @Author: 张晟睿
* @Date: 2021/9/5 21:26
* @Version: 1.0
*/
import java.util.concurrent.TimeUnit;
/**
*@ClassName Test2
*@Description
*@Author 张晟睿
*@Date 2021/9/5
**/
//3、增加一个普通方法后! 发短信还是Hello,发短信 hello
//4、两个对象,两个同步方法,发短信还是打电话 打电话
public class Test2 {
public static void main(String[] args) {
//两个对象,两把锁
Phone2 phone = new Phone2();
Phone2 phone2 = new Phone2();
//锁存在
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone2{
//synchronized锁的对象是方法的调用者!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
//这里没有锁!bubu不是同步方法,不受锁的影响
public void hello(){
System.out.println("hello");
}
}
5-6锁
package com.zmz.lock8;/**
* @ProjectName: Juc
* @Package: com.zmz.lock8
* @ClassName: Test3
* @Author: 张晟睿
* @Date: 2021/9/6 12:35
* @Version: 1.0
*/
import java.util.concurrent.TimeUnit;
/**
*@ClassName Test3
*@Description
*@Author 张晟睿
*@Date 2021/9/6
**/
/*
* 5、增加两个静态同步方法,只有一个对象,先打印发短信还是打电话? 发短信
* 6、两个对象!增加两个静态同步方法,只有一个对象,先打印发短信还是打电话 发短信
* */
public class Test3 {
public static void main(String[] args) {
//两个对象,两个调用者,两把锁!
//两个对象的class类模板只有一个,static,锁的是Class
Phone3 phone = new Phone3();
Phone3 phone2 = new Phone3();
//锁存在
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
//Phone3唯一的一个Class对象
class Phone3{
//synchronized锁的对象是方法的调用者!
//static 静态方法
//类一加载就有了!Class 模板 锁的是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("打电话");
}
}
7-8锁
package com.zmz.lock8;/**
* @ProjectName: Juc
* @Package: com.zmz.lock8
* @ClassName: Test4
* @Author: 张晟睿
* @Date: 2021/9/6 13:07
* @Version: 1.0
*/
import java.util.concurrent.TimeUnit;
/**
*@ClassName Test4
*@Description
*@Author 张晟睿
*@Date 2021/9/6
**/
/*
* 1个静态同步方法,1个同步方法,1个对象 先打印发短信还是打电话? 打电话
* 1个静态同步方法,1个同步方法,2个对象 先打印发短信还是打电话? 发短信
* */
public class Test4 {
public static void main(String[] args) {
//两个对象,两个调用者,两把锁!
//两个对象的class类模板只有一个,static,锁的是Class
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();
}
}
//Phone4唯一的一个Class对象
class Phone4{
//synchronized锁的对象是方法的调用者!
//锁的是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("打电话");
}
}
1、ArrayList集合不安全
package com;/**
* @ProjectName: Juc
* @Package: com
* @ClassName: unsafe
* @Author: 张晟睿
* @Date: 2021/9/6 19:37
* @Version: 1.0
*/
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*@ClassName unsafe
*@Description
*@Author 张晟睿
*@Date 2021/9/6
**/
public class unsafe {
public static void main(String[] args) {
//高并发下的ArrayList真的安全么?
/**
* 解决方法
* 1、List list = new Vector();
* 2、List list = Collections.synchronizedList(new ArrayList<>());
* 3、List list = new CopyOnWriteArrayList<>();
* */
List<String> list = new CopyOnWriteArrayList<>();
//启动10个多线程
for (int i = 1; i < 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
CopyOnWriteArrayList源码分析
2、Set不安全
package com.zmz.unsafe;/**
* @ProjectName: Juc
* @Package: com.zmz.unsafe
* @ClassName: SetSafe
* @Author: 张晟睿
* @Date: 2021/9/6 21:20
* @Version: 1.0
*/
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
*@ClassName SetSafe
*@Description
*@Author 张晟睿
*@Date 2021/9/6
**/
public class SetSafe {
public static void main(String[] args) {
//Set set = new HashSet<>();
//Set set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i < 60; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}
HashSet源码
public HashSet() {
map = new HashMap<>();
}
//HashSet本质就是Map集合
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object();//不变的值
3、HashMap不安全
package com.zmz.unsafe;/**
* @ProjectName: Juc
* @Package: com.zmz.unsafe
* @ClassName: MapSafe
* @Author: 张晟睿
* @Date: 2021/9/6 21:27
* @Version: 1.0
*/
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
*@ClassName MapSafe
*@Description
*@Author 张晟睿
*@Date 2021/9/6
**/
public class MapSafe {
public static void main(String[] args) {
//Map map = new HashMap<>();
//Map map = Collections.synchronizedMap(new HashMap<>());
Map<String, Object> map = new ConcurrentHashMap<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
callable源码
代码测试
package com.zmz.callable;/**
* @ProjectName: Juc
* @Package: com.zmz.callable
* @ClassName: CallableTest
* @Author: 张晟睿
* @Date: 2021/10/3 16:52
* @Version: 1.0
*/
import com.sun.org.apache.bcel.internal.generic.NEW;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
*@ClassName CallableTest
*@Description
*@Author 张晟睿
*@Date 2021/10/3
**/
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new Runnable()).start();
// new Thread(new FutureTask()).start();
// new Thread(new FutureTask( Callable )).start();
new Thread().start();
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask(myThread);//适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start(); //结果存在缓存提交效率
Integer o = (Integer) futureTask.get();//获取返回的结果
//这个get方法可以会产生阻塞! 解决办法 放到最后一行或者异步通信
System.out.println(o);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("call()方法");
//耗时操作
return 1024;
}
}
/*
1、有缓存
2、结果可能会等待,阻塞
*/
package com.zmz.assist;/**
* @ProjectName: Juc
* @Package: com.zmz.assist
* @ClassName: CountDownLatchDemo
* @Author: 张晟睿
* @Date: 2021/10/3 18:00
* @Version: 1.0
*/
import java.util.concurrent.CountDownLatch;
/**
*@ClassName CountDownLatchDemo
*@Description
*@Author 张晟睿
*@Date 2021/10/3
**/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch count = new CountDownLatch(10);
for (int i = 1; i <= 10; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName() + "Go out");
count.countDown();//数量-1
},String.valueOf(i)).start();
}
count.await();//等计数器归零,然后再往下执行
System.out.println("Close Door");
}
}
原理:
每次有线程用countDown()数量-1,如果计算器变为0了,然后count.await()就被唤醒,继续下面的执行!
package com.zmz.assist;/**
* @ProjectName: Juc
* @Package: com.zmz.assist
* @ClassName: CycilcBarrierDemo
* @Author: 张晟睿
* @Date: 2021/10/3 18:23
* @Version: 1.0
*/
import com.sun.org.apache.bcel.internal.generic.NEW;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
*@ClassName CycilcBarrierDemo
*@Description
*@Author 张晟睿
*@Date 2021/10/3
**/
public class CycilcBarrierDemo {
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(7,()->{
System.out.println("你已经凑齐了七颗龙珠!可以变身了");
});
for (int i = 1; i <= 7; i++) {
final int temp = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName()+ "收集" +(temp) +"颗龙珠");
try {
barrier.await();//等待
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
Semaphore:信号量,很多时候用来处理高并发。
package com.zmz.assist;/**
* @ProjectName: Juc
* @Package: com.zmz.assist
* @ClassName: SemaphoreDemo
* @Author: 张晟睿
* @Date: 2021/10/3 19:32
* @Version: 1.0
*/
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
*@ClassName SemaphoreDemo
*@Description
*@Author 张晟睿
*@Date 2021/10/3
**/
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
semaphore.acquire();//获得
System.out.println(Thread.currentThread().getName()+"得到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();//释放
}
},String.valueOf(i)).start();
}
}
}
原理:
acquire():获得,假设已经满了组需要等待,直到被释放为止
release():释放,会将当前的信号量释放+1,然后唤醒等待的线程!
作用:多个共享的资源互斥使用!并发限流控制最大的线程数量!
package com.zmz.lock;/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: ReadwritelockDemo
* @Author: 张晟睿
* @Date: 2021/10/3 20:48
* @Version: 1.0
*/
import java.util.HashMap;
import java.util.Map;
/**
*@ClassName ReadwritelockDemo
*@Description
*@Author 张晟睿
*@Date 2021/10/3
**/
public class ReadwritelockDemo {
public static void main(String[] args) {
Mycache mycache = new Mycache();
//写入
for (int i = 1; i < 6; i++) {
final int temp = i;
new Thread(()->{
mycache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
//读取
for (int i = 1; i < 6; i++) {
final int temp = i;
new Thread(()->{
mycache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class Mycache{
private volatile Map<String , Object> map = new HashMap<>();
public void put(String key, Object value){
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key,value);
System.out.println(Thread.currentThread().getName() + "写入完成");
}
public void get(String key){
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完成");
}
}
/*
1写入1
5写入5
5写入完成
4写入4
4写入完成
3写入3
2写入2
1读取1
3写入完成
1写入完成
2读取2
2读取完成
3读取3
3读取完成
1读取完成
2写入完成
5读取5
5读取完成
4读取4
4读取完成
*/
我们可以看到出现了严重的插队问题!该如何去解决囊?我们使用读写锁来解决插队的问题。
修改后的操作
package com.zmz.lock;/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: ReadwritelockDemo
* @Author: 张晟睿
* @Date: 2021/10/3 20:48
* @Version: 1.0
*/
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @ClassName ReadwritelockDemo
* @Description
* @Author 张晟睿
* @Date 2021/10/3
**/
/*
*独占锁 (写锁)一次只能被一个线程占有
*共享锁 (读锁)多个线程可以同时占有
* ReadWriteLock
* 读-读 可以共存
* 读-写 不可以共存
* 写-写 不可以共存
* */
public class ReadwritelockDemo {
public static void main(String[] args) {
MycacheLock mycache = new MycacheLock();
//写入
for (int i = 1; i < 6; i++) {
final int temp = i;
new Thread(() -> {
mycache.put(temp + "", temp + "");
}, String.valueOf(i)).start();
}
//读取
for (int i = 1; i < 6; i++) {
final int temp = i;
new Thread(() -> {
mycache.get(temp + "");
}, String.valueOf(i)).start();
}
}
}
//加锁
class MycacheLock {
private volatile Map<String, Object> map = new HashMap<>();
//读写锁:更加细粒度的控制
ReadWriteLock 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(Thread.currentThread().getName() + "写入完成");
} 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();
}
}
}
class Mycache {
private volatile Map<String, Object> map = new HashMap<>();
public void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完成");
}
public void get(String key) {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完成");
}
}
/*
1写入1
1写入完成
2写入2
2写入完成
3写入3
3写入完成
4写入4
4写入完成
5写入5
5写入完成
1读取1
1读取完成
5读取5
3读取3
3读取完成
4读取4
4读取完成
5读取完成
2读取2
2读取完成
*/
我们可以看到输出的结果在写入的时候有序的进行,读操作的时候可以无序的进行,可以看到已经到达我们预期的效果
堵塞
什么情况下我们使用阻塞队列:多线程并发处理,线程池!
10.1 学会使用队列
添加、移除元素,现在有四组API
方法 | 抛出异常 | 不会抛出异常,有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer(E e, long timeout, TimeUnit unit) |
移除 | remove() | poll() | take() | poll(long timeout, TimeUnit unit) |
判断首部 | element() | peek() | - | - |
/*
* 抛出异常
* */
public static void test1(){
//队列大小3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//IllegalState ExceptionQueue full 抛出异常
//System.out.println(blockingQueue.add("d"));
//检测队首元素
System.out.println(blockingQueue.element()); System.out.println("================================================");
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//java.util.NoSuchElementException 抛出异常
//System.out.println(blockingQueue.remove());
}
/*
* 不抛出异常,有返回值
* */
public static void test2(){
//队列大小3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//false
System.out.println(blockingQueue.offer("d"));
//检测队首元素
System.out.println(blockingQueue.peek());
System.out.println("================================================");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
//null
System.out.println(blockingQueue.poll());
}
/*
* 阻塞等待之☞死死的等待
* */
public static void test3() throws InterruptedException {
//队列大小3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d");
//无检测队首元素
System.out.println("================================================");
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 {
//队列大小3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
blockingQueue.offer("d",2, TimeUnit.SECONDS);
//无检测队首元素
System.out.println("================================================");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
blockingQueue.poll(2,TimeUnit.SECONDS);
}
没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个元素
put take
package com.zmz.queue;
/**
* @ProjectName: Juc
* @Package: com.zmz.queue
* @ClassName: SyncQueue
* @Author: 张晟睿
* @Date: 2021/10/8 14:18
* @Version: 1.0
*/
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 同步队列
* 和其他的lockQueue 不一样, SynchronousQueue 不存储元素
*/
public class SyncQueue {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>(); //同步队列
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName() + "put 1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName() + "put 2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName() + "put 3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Thread1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "=>" + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "=>" + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "=>" + synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
},"Thread2").start();
}
}
/*
Thread1put 1
Thread2=>1
Thread1put 2
Thread2=>2
Thread1put 3
Thread2=>3
*/
线程池有三大方法,七大参数,四种拒绝策略
程序的运行,本质: 占用系统的资源 ! 优化CPU资源的使用 ===>池化技术
线程池, 连接池, 内存池, 对象池///…
池化技术: 实现准备好一些资源, 有人要用,就来我这里拿,用完之后还给我
线程复用,可以控制最大并发数,管理线程
package com.zmz.Pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ProjectName: Juc
* @Package: com.zmz.ThreadPool
* @ClassName: ThreadPool
* @Author: 张晟睿
* @Date: 2021/10/8 16:44
* @Version: 1.0
*/
public class ThreadPool {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
ExecutorService threadPool2 = Executors.newFixedThreadPool(5); //创建一个固定的线程池的大小
ExecutorService threadPool3 = Executors.newCachedThreadPool(); //可伸缩的
//线程池用完必须要关闭线程池
try {
for (int i = 1; i <=100 ; i++) {
//通过线程池创建线程
threadPool3.execute(()->{
System.out.println(Thread.currentThread().getName()+ " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool3.shutdown();
}
}
}
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
package com.zmz.Pool;
import java.util.concurrent.*;
/**
* @ProjectName: Juc
* @Package: com.zmz.Pool
* @ClassName: ThreadPoolExecutorTest
* @Author: 张晟睿
* @Date: 2021/10/8 16:59
* @Version: 1.0
*/
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
// 获取cpu 的核数
int max = Runtime.getRuntime().availableProcessors();
ExecutorService service =new ThreadPoolExecutor(
2,//核心线程池大小
max,//最大的线程池大小
3,//超时了没有人调用就会释放
TimeUnit.SECONDS,//超时单位
new LinkedBlockingDeque<>(3),//阻塞队列
Executors.defaultThreadFactory(),//线程工厂 创建线程的
new ThreadPoolExecutor.AbortPolicy()//拒绝策略
);
try {
for (int i = 1; i <= 5; i++) {
service.execute(() -> {
System.out.println(Thread.currentThread().getName() + "运行成功");
});
}
}catch (Exception e) {
e.printStackTrace();
}
finally {
service.shutdown();
}
}
}
- new ThreadPoolExecutor.AbortPolicy() 超出最大处理线程抛出异常
- new ThreadPoolExecutor.CallerRunsPolicy() 从哪个线程创建就由那个线程执行
- new ThreadPoolExecutor.DiscardPolicy() 队列满了不会抛出异常
- new ThreadPoolExecutor.DiscardOldestPolicy() 尝试去和第一个竞争,也不会抛出异常
新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算
package com.zmz.FourFunction;
import java.util.function.Function;
/**
* @ProjectName: Juc
* @Package: com.zmz.FourFunction
* @ClassName: functionDemo
* @Author: 张晟睿
* @Date: 2021/10/8 17:15
* @Version: 1.0
*/
public class functionDemo {
public static void main(String[] args) {
Function<String, String> function = (str) -> {
return str;
};
System.out.println(function.apply("Hello,zmz!"));
}
}
package com.zmz.FourFunction;
import java.util.function.Predicate;
/**
* @ProjectName: Juc
* @Package: com.zmz.FourFunction
* @ClassName: PredicateDemo
* @Author: 张晟睿
* @Date: 2021/10/8 17:18
* @Version: 1.0
*/
public class PredicateDemo {
public static void main(String[] args) {
Predicate<String> predicate = (str) -> {
return str.isEmpty();};
// false
System.out.println(predicate.test("zmz"));
// true
System.out.println(predicate.test(""));
}
}
package com.zmz.FourFunction;
import java.util.function.Supplier;
/**
* @ProjectName: Juc
* @Package: com.zmz.FourFunction
* @ClassName: SuppierDemo
* @Author: 张晟睿
* @Date: 2021/10/8 17:21
* @Version: 1.0
*/
public class SuppierDemo {
public static void main(String[] args) {
Supplier<String> supplier = ()->{
return "1024";};
System.out.println(supplier.get());
}
}
package com.zmz.FourFunction;
import java.util.function.Consumer;
/**
* @ProjectName: Juc
* @Package: com.zmz.FourFunction
* @ClassName: ConsummerDemo
* @Author: 张晟睿
* @Date: 2021/10/8 17:21
* @Version: 1.0
*/
public class ConsummerDemo {
public static void main(String[] args) {
Consumer<String> consumer = (str)->{
System.out.println(str);
};
consumer.accept("zmz");
}
}
package com.zmz.Stream;
/**
* @ProjectName: Juc
* @Package: com.zmz.Stream
* @ClassName: User
* @Author: 张晟睿
* @Date: 2021/10/8 18:01
* @Version: 1.0
*/
public class User {
private int id;
private String name;
private int age;
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
}
package com.zmz.Stream;
/**
* @ProjectName: Juc
* @Package: com.zmz.Stream
* @ClassName: StreamDemo
* @Author: 张晟睿
* @Date: 2021/10/8 18:00
* @Version: 1.0
*
* * 题目要求: 用一行代码实现
* * 1. Id 必须是偶数
* * 2.年龄必须大于23
* * 3. 用户名转为大写
* * 4. 用户名倒序
* * 5. 只能输出一个用户
*/
import java.util.Arrays;
import java.util.List;
public class StreamDemo {
public static void main(String[] args) {
User u1 = new User(1, "a", 23);
User u2 = new User(2, "b", 23);
User u3 = new User(3, "c", 23);
User u4 = new User(6, "d", 24);
User u5 = new User(4, "e", 25);
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
// lambda、链式编程、函数式接口、流式计算
list.stream()
.filter(user -> {
return user.getId()%2 == 0;})
.filter(user -> {
return user.getAge() > 20;})
.map(user -> {
return user.getName().toUpperCase();})
.sorted((user1, user2) -> {
return user2.compareTo(user1);})
.limit(1)
.forEach(System.out::println);
}
}
什么是ForkJoin?
ava.util.concurrent.ForkJoinPool由Java大师Doug Lea主持编写,它可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。本文中对Fork/Join框架的讲解,基于JDK1.8+中的Fork/Join框架实现,参考的Fork/Join框架主要源代码也基于JDK1.8+。
这几篇文章将试图解释Fork/Join框架的知识点,以便对自己、对各位读者在并发程序的设计思路上进行一些启发。文章将首先讲解Fork/Join框架的基本使用,以及其中需要注意的使用要点;接着使用Fork/Join框架解决一些实际问题;最后再讲解Fork/Join框架的工作原理。
第一步,通过ForkJoinPool来执行
第二步,计算任务 execute(ForkJoinTask> task)
第三步,计算类要去继承ForkJoinTask
ForkJoin 的计算类
ForkJoinComputer.java
package com.zmz.ForkJoin;
import java.util.concurrent.RecursiveTask;
/**
* @ProjectName: Juc
* @Package: com.zmz.ForkJoin
* @ClassName: ForkJoinComputer
* @Author: 张晟睿
* @Date: 2021/10/9 15:17
* @Version: 1.0
*/
public class ForkJoinComputer extends RecursiveTask<Long> {
private long start;
private long end;
/** 临界值 */
private long temp = 1000000L;
public ForkJoinComputer(long start, long end) {
this.start = start;
this.end = end;
}
/**
* 计算方法
* @return
*/
@Override
protected Long compute() {
if ((end - start) < temp) {
Long sum = 0L;
for (Long i = start; i < end; i++) {
sum += i;
}
return sum;
}else {
// 使用ForkJoin 分而治之 计算
//1 . 计算平均值
long middle = (start + end) / 2;
ForkJoinComputer forkJoinDemo1 = new ForkJoinComputer(start, middle);
// 拆分任务,把线程压入线程队列
forkJoinDemo1.fork();
ForkJoinComputer forkJoinDemo2 = new ForkJoinComputer(middle, end);
forkJoinDemo2.fork();
long taskSum = forkJoinDemo1.join() + forkJoinDemo2.join();
return taskSum;
}
}
}
测试类 ForkJoinTest.java
package com.zmz.ForkJoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/**
* @ProjectName: Juc
* @Package: com.zmz.ForkJoin
* @ClassName: ForkJoinTest
* @Author: 张晟睿
* @Date: 2021/10/9 15:18
* @Version: 1.0
*/
public class ForkJoinTest {
private static final long SUM = 20_0000_0000;
public static void main(String[] args) throws ExecutionException, InterruptedException {
test1();
test2();
test3();
}
/**
* 使用普通方法
*/
public static void test1() {
long star = System.currentTimeMillis();
long sum = 0L;
for (long i = 1; i < SUM ; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println(sum);
System.out.println("普通程序猿——时间:" + (end - star));
System.out.println("============================");
}
/**
* 使用ForkJoin 方法
*/
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinComputer(0L, SUM);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long along = submit.get();
System.out.println(along);
long end = System.currentTimeMillis();
System.out.println("中级程序猿——时间:" + (end - start));
System.out.println("--------------");
}
/**
* 使用 Stream 流计算
*/
public static void test3() {
long start = System.currentTimeMillis();
long sum = LongStream.range(0L, 20_0000_0000L).parallel().reduce(0, Long::sum);
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.println("高级程序猿——时间:" + (end - start));
System.out.println("--------------");
System.out.println("============================");
}
}
分析一下高级程序猿的处理:
.parallel().reduce(0, Long::sum)使用一个并行流去计算整个计算,提高效率。
附一张学妹的照片
⭐学妹都已经收藏了!!!⭐