博客主页:作者主页
简介: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 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 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 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{ @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 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写入15写入55写入完成4写入44写入完成3写入32写入21读取13写入完成1写入完成2读取22读取完成3读取33读取完成1读取完成2写入完成5读取55读取完成4读取44读取完成*/
我们可以看到出现了严重的插队问题!该如何去解决囊?我们使用读写锁来解决插队的问题。
修改后的操作
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 1Thread2=>1Thread1put 2Thread2=>2Thread1put 3Thread2=>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 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)使用一个并行流去计算整个计算,提高效率。
Future 设计的初衷:对将来的某个事件结果进行建模!
其实就是前端 —》发送ajax异步请求给后端
但是我们平时都使用CompletableFuture
package com.zmz.Async;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;/** * @ProjectName: Juc * @Package: com.zmz.Async * @ClassName: runAsync * @Author: 张晟睿 * @Date: 2021/10/11 18:58 * @Version: 1.0 */public class runAsync { public static void main(String[] args) throws ExecutionException, InterruptedException { // 发起 一个 请求 System.out.println(System.currentTimeMillis()); System.out.println("---------------------"); CompletableFuture future = CompletableFuture.runAsync(()->{ //发起一个异步任务 try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"....."); }); System.out.println(System.currentTimeMillis()); System.out.println("------------------------------"); //输出执行结果 System.out.println(future.get()); //获取执行结果 }}
package com.zmz.Async;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;/** * @ProjectName: Juc * @Package: com.zmz.Async * @ClassName: supplyAsync * @Author: 张晟睿 * @Date: 2021/10/11 19:09 * @Version: 1.0 */public class supplyAsync { public static void main(String[] args) throws ExecutionException, InterruptedException { //有返回值的异步回调 CompletableFuture completableFuture=CompletableFuture.supplyAsync(()->{ System.out.println(Thread.currentThread().getName()); try { TimeUnit.SECONDS.sleep(2); int i=1/0; } catch (InterruptedException e) { e.printStackTrace(); } return 1024; }); System.out.println(completableFuture.whenComplete((t, u) -> { /*我们可以看到whenComplete以上的程序有两个参数,一个是t 一个是u T:是代表的 正常返回的结果; U:是代表的 抛出异常的错误信息; 如果发生了异常,get可以获取到exceptionally返回的值; */ //success 回调 System.out.println("t=>" + t); //正常的返回结果 System.out.println("u=>" + u); //抛出异常的 错误信息 }).exceptionally((e) -> { //error回调 System.out.println(e.getMessage()); return 404; }).get()); }}
JMM:JAVA内存模型,不存在的东西,抽象的,是一个概念,也是一个约定!
关于JMM的一些同步的约定:
线程中分为 工作内存、主内存。
八种操作:
名称 | 描述 |
---|---|
Read(读取) | 作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用 |
load(载入) | 作用于工作内存的变量,它把read操作从主存中变量放入工作内存中 |
Use(使用) | 作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令 |
assign(赋值) | 作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中 |
store(存储) | 作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用 |
write(写入) | 作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中 |
lock(锁定) | 作用于主内存的变量,把一个变量标识为线程独占状态 |
unlock(解锁) | 作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yTIjbiQ8-1635035368123)(https://gitee.com/z6135/cloudimage/raw/master/img/juc1 (2)].png)
对于八种操作给了相应的规定:
遇到问题:程序不知道主存中的值已经被修改过了!
package com.zmz.JMM;import java.util.concurrent.TimeUnit;/** * @ProjectName: Juc * @Package: com.zmz.JMM * @ClassName: JMMdemo01 * @Author: 张晟睿 * @Date: 2021/10/11 20:43 * @Version: 1.0 */public class JMMdemo01 { // 如果不加volatile 程序会死循环 // 加了volatile是可以保证可见性的 private volatile static Integer number = 0; public static void main(String[] args) { //main线程 //子线程1 new Thread(()->{ while (number==0){ } }).start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } //子线程2 new Thread(()->{ while (number==0){ } }).start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } number=1; System.out.println(number); }}
原子性:意思就是说不可分割,举一个例子就是说线程A在执行任务的时候,不能被打扰的,也不能被分割的,要么同时成功,要么同时失败。
package com.zmz.JMM;/** * @ProjectName: Juc * @Package: com.zmz.JMM * @ClassName: JMMdemo02 * @Author: 张晟睿 * @Date: 2021/10/11 20:46 * @Version: 1.0 */public class JMMdemo02 { private static volatile int num = 0; public static void add(){ num++; //++ 不是一个原子性操作,是2个~3个操作 } public static void main(String[] args) { //理论上number === 20000 for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 1; j <= 1000 ; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ //main gc Thread.yield(); } System.out.println(Thread.currentThread().getName()+",num="+num); }}
使用原子类
package com.zmz.JMM;import java.util.concurrent.atomic.AtomicInteger;/** * @ProjectName: Juc * @Package: com.zmz.JMM * @ClassName: JMMdemo03 * @Author: 张晟睿 * @Date: 2021/10/11 21:05 * @Version: 1.0 */public class JMMdemo03 { private static volatile AtomicInteger number = new AtomicInteger(); public static void add(){// number++; number.incrementAndGet(); //底层是CAS保证的原子性 } public static void main(String[] args) { //理论上number === 20000 for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 1; j <= 1000 ; j++) { add(); } }).start(); } while (Thread.activeCount()>2){ //main gc Thread.yield(); } System.out.println(Thread.currentThread().getName()+",num="+number); }}
这些类的底层都直接和操作系统挂钩!是在内存中修改值。
什么是指令重排?
我们写的程序,计算机并不是按照我们自己写的那样去执行的
源代码–>编译器优化重排–>指令并行也可能会重排–>内存系统也会重排–>执行
处理器在进行指令重排的时候,会考虑数据之间的依赖性!
int x=2; //1
int y=4; //2
x=x+10; //3
y=x*x; //4
//我们期望的执行顺序是 1_2_3_4 可能执行的顺序会变成3124 1423
//可不可能是 4123? 不可能的
1234567
可能造成的影响结果:前提:a b x y这四个值 默认都是0
线程A | 线程B |
---|---|
x=a | y=b |
b=1 | a=2 |
正常的结果: x = 0; y =0
线程A | 线程B |
---|---|
b=1 | a=2 |
x=a | y=b |
可能在线程A中会出现,先执行b=1,然后再执行x=a
在B线程中可能会出现,先执行a=2,然后执行y=b
那么就有可能结果如下:x=4; y=2
volatile可以避免指令重排:
volatile中会加一道内存的屏障,这个内存屏障可以保证在这个屏障中的指令顺序。
内存屏障:CPU指令。作用:保证特定的操作的执行顺序;可以保证某些变量的内存可见性(利用这些特性,就可以保证volatile实现的可见性)
单例模式
package single;
//饿汉式单例模式
@SuppressWarnings("all")
public class Hungry {
private byte[] date1= new byte[1024*1024];
private byte[] date2= new byte[1024*1024];
private byte[] date3= new byte[1024*1024];
private byte[] date4= new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
package single;
import java.lang.reflect.Constructor;
//懒汉式
public class LazyMan {
private LazyMan(){
synchronized(LazyMan.class){
throw new RuntimeException("不要试图使用反射破坏异常");
}
// System.out.println(Thread.currentThread().getName()+"ok");
}
private static LazyMan lazyMan;
//双重检测锁
public static LazyMan getInstance(){
if (lazyMan==null) {
synchronized (LazyMan.class) {
if (lazyMan==null) {
lazyMan = new LazyMan();
//不是原子性操作
//1.分配内存空间
//2.执行构造方法,初始化对象
//3.把这个对象指向空间
}
}
}
return lazyMan;
}
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
for (int i = 0; i < 2; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
package single;
public class Holder {
private Holder(){}
private static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass {
private static final Holder HOLDER = new Holder();
}
}
单例不安全, 主要的原因是因为反射。
package com.zmz.Singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @ProjectName: Juc
* @Package: com.zmz.Singleton
* @ClassName: EnumSingle
* @Author: 张晟睿
* @Date: 2021/10/12 20:06
* @Version: 1.0
*/
//enum 是什么? enum本身就是一个Class 类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
//java.lang.NoSuchMethodException: com.ogj.single.EnumSingle.()
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
枚举类型的最终反编译源码:
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/ogj/single/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}
如果想要深入了解CAS,请跳转到以下文章
【高频面试】锁与CAS详解(⭐建议收藏)_
CAS(Compare And Swap比较并替换),包含三个值当前内存值(V)、预期原来的值(A)以及期待更新的值(B),这里我们先做简单的介绍后期会出一期博文进行单独介绍的。
package com.zmz.CAS;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ProjectName: Juc
* @Package: com.zmz.CAS
* @ClassName: CasDemo01
* @Author: 张晟睿
* @Date: 2021/10/12 20:15
* @Version: 1.0
*/
public class CasDemo01 {
//CAS : compareAndSet 比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//boolean compareAndSet(int expect, int update)
//期望值、更新值
//如果实际值 和 我的期望值相同,那么就更新
//如果实际值 和 我的期望值不同,那么就不更新
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
//因为期望值是2020 实际值却变成了2021 所以会修改失败
//CAS 是CPU的并发原语
atomicInteger.getAndIncrement(); //++操作
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
我们点开AtomicInteger源码观察一下,可以发现
CAS:比较当前工作内存中的值 和 主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环,使用的是自旋锁。
缺点:
什么是ABA问题?(就是我们所说的狸猫换太子)
package com.zmz.CAS;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ProjectName: Juc
* @Package: com.zmz.CAS
* @ClassName: CasDemo02
* @Author: 张晟睿
* @Date: 2021/10/12 20:40
* @Version: 1.0
*/
public class CasDemo02 {
//CAS : compareAndSet 比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
//boolean compareAndSet(int expect, int update)
//期望值、更新值
//如果实际值 和 我的期望值相同,那么就更新
//如果实际值 和 我的期望值不同,那么就不更新
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
//因为期望值是2020 实际值却变成了2021 所以会修改失败
//CAS 是CPU的并发原语
// atomicInteger.getAndIncrement(); //++操作
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
为了解决ABA问题,对应的思想:就是使用了乐观锁~
注意:
Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。
说明:对于Integer var = ?在-128 至127之间的赋值,Integer对象是在IntegerCache .cache产生,会复用已有对象, 这个区间内的Integer值可以直接使用==进 行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。
package com.zmz.CAS;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* @ProjectName: Juc
* @Package: com.zmz.CAS
* @ClassName: CasDemo03
* @Author: 张晟睿
* @Date: 2021/10/12 20:45
* @Version: 1.0
*/
public class CasDemo03 {
/**AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
* 正常在业务操作,这里面比较的都是一个个对象
*/
static AtomicStampedReference<Integer> atomicStampedReference = new
AtomicStampedReference<>(1, 1);
// CAS compareAndSet : 比较并交换!
public static void main(String[] args) {
new Thread(() -> {
int stamp = atomicStampedReference.getStamp(); // 获得版本号
System.out.println("a1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 修改操作时,版本号更新 + 1
atomicStampedReference.compareAndSet(1, 2,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1);
System.out.println("a2=>" + atomicStampedReference.getStamp());
// 重新把值改回去, 版本号更新 + 1
System.out.println(atomicStampedReference.compareAndSet(2, 1,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1));
System.out.println("a3=>" + atomicStampedReference.getStamp());
}, "a").start();
// 乐观锁的原理相同!
new Thread(() -> {
int stamp = atomicStampedReference.getStamp(); // 获得版本号
System.out.println("b1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(1, 3,
stamp, stamp + 1));
System.out.println("b2=>" + atomicStampedReference.getStamp());
}, "b").start();
}
}
1.Synchonized锁
package com.zmz.lock;
/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: SynchonizedDemo
* @Author: 张晟睿
* @Date: 2021/10/12 20:59
* @Version: 1.0
*/
public class SynchonizedDemo {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+"=> 发短息");
call();//这里也有一把锁
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()+"=> 打电话");
}
}
2.Lock锁
package com.zmz.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: LockDemo
* @Author: 张晟睿
* @Date: 2021/10/12 21:02
* @Version: 1.0
*/
public class LockDemo {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone2{
Lock lock=new ReentrantLock();
public void sms(){
lock.lock(); //细节:这个是两把锁,两个钥匙
//lock锁必须配对,否则就会死锁在里面
try {
System.out.println(Thread.currentThread().getName()+"=> 发短信");
call();//这里也有一把锁
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void call(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "=> 打电话");
}catch (Exception e){
e.printStackTrace();
}
finally {
lock.unlock();
}
}
}
注意:
1.spinlock
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5;}
2.自我设计自旋锁
package com.zmz.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: MySpinlockTest
* @Author: 张晟睿
* @Date: 2021/10/12 21:07
* @Version: 1.0
*/
class MySpinlock {
// 默认
// int 0
//thread null
AtomicReference<Thread> atomicReference=new AtomicReference<>();
//加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"===> mylock");
//自旋锁
while (!atomicReference.compareAndSet(null,thread)){
System.out.println(Thread.currentThread().getName()+" ==> 自旋中~");
}
}
//解锁
public void myUnlock(){
Thread thread=Thread.currentThread();
System.out.println(thread.getName()+"===> myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
public class MySpinlockTest {
public static void main(String[] args) throws InterruptedException {
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
reentrantLock.unlock();
//使用CAS实现自旋锁
MySpinlock spinlockDemo=new MySpinlock();
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"Thread1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
spinlockDemo.myUnlock();
}
},"Thread2").start();
}
}
运行结果:t2进程必须等待t1进程Unlock后,才能Unlock,在这之前进行自旋等待。
package com.zmz.lock;
import java.util.concurrent.TimeUnit;
/**
* @ProjectName: Juc
* @Package: com.zmz.lock
* @ClassName: DeadLockDemo
* @Author: 张晟睿
* @Date: 2021/10/12 21:13
* @Version: 1.0
*/
public class DeadLockDemo {
public static void main(String[] args) {
String lockA= "lockA";
String lockB= "lockB";
new Thread(new MyThread1(lockA,lockB),"Thread1").start();
new Thread(new MyThread1(lockB,lockA),"Thread2").start();
}
}
class MyThread1 implements Runnable{
private String lockA;
private String lockB;
public MyThread1(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+" lock"+lockA+"===>get"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===>get"+lockA);
}
}
}
}
1、使用jps定位进程号,jdk的bin目录下: 有一个jps
jps -l
2、进程进程号 找到死锁信息
jstack 进程号 #如果出现拒绝访问 一定要以管理员身份运行
一般情况信息在最后: