中色十二冶金建设有限公司网站,扬中网站建设怎么样,传奇网页链接,全景网站开发多少钱基于bilibili狂神说JUC并发编程视频所做笔记
概述
什么是JUC
JUC时java.util工具包中的三个包的简称 java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks 业务#xff1a;普通的线程代码中#xff0c;我们常使用Runnable接口
但Runnable没有返…基于bilibili狂神说JUC并发编程视频所做笔记
概述
什么是JUC
JUC时java.util工具包中的三个包的简称 java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks 业务普通的线程代码中我们常使用Runnable接口
但Runnable没有返回值且效率相比较于Callable来说相对较低功能也没有Callable强大
线程和进程
进程相当于一个程序
一个进程当中往往可以包含多个线程且至少包含一个线程
Java默认有2个线程mian主线程GC垃圾回收
Java真的可以开启线程吗
java是无法开启线程的Java运行在JVM虚拟机之上无法直接操作硬件因此其实际上是无法开启线程的在我们无论使用Runnable接口还是继承Thread用start()方法开启线程其本质上都是调用private native void start0()方法而该方法是本地方法是运行底层的C
并发编程
并发与并行
并发多个线程同时操作一个核
CPU一核快速交替轮换模拟多核
并行多个线程操作多个核
CPU多核可以多个线程同时执行引入线程池的概念
查看自己CPU核数
public class Test1 {public static void main(String[] args) {// 获取CPU的核数// COU 密集型IO密集型System.out.println(Runtime.getRuntime().availableProcessors());}
}并发编程的本质充分利用CPU的资源 线程有几个状态 答 6个分别为NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED
public enum State{/*** Thread state for a thread which has not yet started.*/
NEW,/*** Thread state for a runnable thread. A thread in the runnable* state is executing in the Java virtual machine but it may* be waiting for other resources from the operating system* such as processor.*/
RUNNABLE,/*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {link Object#wait() Object.wait}.*/
BLOCKED,/*** Thread state for a waiting thread.* A thread is in the waiting state due to calling one of the* following methods:* ul* li{link Object#wait() Object.wait} with no timeout/li* li{link #join() Thread.join} with no timeout/li* li{link LockSupport#park() LockSupport.park}/li* /ul** pA thread in the waiting state is waiting for another thread to* perform a particular action.** For example, a thread that has called {code Object.wait()}* on an object is waiting for another thread to call* {code Object.notify()} or {code Object.notifyAll()} on* that object. A thread that has called {code Thread.join()}* is waiting for a specified thread to terminate.*/
WAITING,/*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* ul* li{link #sleep Thread.sleep}/li* li{link Object#wait(long) Object.wait} with timeout/li* li{link #join(long) Thread.join} with timeout/li* li{link LockSupport#parkNanos LockSupport.parkNanos}/li* li{link LockSupport#parkUntil LockSupport.parkUntil}/li* /ul*/
TIMED_WAITING,/*** Thread state for a terminated thread.* The thread has completed execution.*/
TERMINATED;
}
WAITING 与 TIMED_WAITING的区别为
WAITING会一直等待唤醒或其他线程资源的响应
TIMED_WAITING为超时等待一旦时间到则不再等待 wait/sleep的区别 来自不同的类
wait 来自Object类
sleep来自Thread类
关于锁的释放
wait会释放锁
sleep不会释放锁抱着锁睡觉
使用的范围是不同的
wait 必须在同步代码块中使用
sleep 可以在任何地方睡
是否需要捕获异常
wait不需要捕获异常
sleep必须要捕获异常
但是只要与线程有关的操作都要捕获中断异常
Lock锁重点
回顾synchronized 传统Synchronized package syn;// OOP并发编程
public class SaleTicketDemo01 {public static void main(String[] args) {// 声明一个票对象使3个线程可以调用买票方法Ticket ticket new Ticket();// 使用lambda表达式回顾lambda表达式是一种极简的表达艺术但仅用于函数式接口new Thread(()-{for (int i 0; i 40; i) {ticket.sale();}},A).start();new Thread(()-{for (int i 0; i 40; i) {ticket.sale();}},B).start();new Thread(()-{for (int i 0; i 40; i) {ticket.sale();}},C).start();}
}// 高耦合对象及为对象不要附加多余功能不要将其变成线程类
class Ticket{// 属性、方法private int ticketNums 30;// synchronized 本质就是锁队列public synchronized void sale(){if (ticketNums 0) {System.out.println(Thread.currentThread().getName() sales ticketNums--,and remainsticketNums);}}
}Lock锁实现线程安全示例 java.util.concurrent.locks下有三个接口 ConditionLockReadWriteLock Lock接口 实现类
ReentranLock可重入锁ReentrantReadWriteLock.ReadLock读锁ReentrantReadWriteLock.WriteLock写锁
在ReentrantLock中其构造函数 公平锁先到先得
非公平锁默认可以插队看CPU调度
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockDemo02 {public static void main(String[] args) {// 声明一个票对象使3个线程可以调用买票方法Ticket ticket new Ticket();// 使用lambda表达式回顾lambda表达式是一种极简的表达艺术但仅用于函数式接口new Thread(()-{for (int i 0; i 40; i) ticket.sale();},A).start();new Thread(()-{for (int i 0; i 40; i) ticket.sale();},B).start();new Thread(()-{for (int i 0; i 40; i) ticket.sale();},C).start();}
}// 高耦合对象及为对象不要附加多余功能不要将其变成线程类
// 使用lock锁
/*
Lock三部曲
1、 new ReentrantLock()
2、 lock.lock()
3、 finally lock.unlock()*/
class Ticket{// 属性、方法private int ticketNums 30;Lock lock new ReentrantLock();// synchronized 本质就是锁队列public void sale(){lock.lock();// 加锁try {//业务代码if (ticketNums 0) {System.out.println(Thread.currentThread().getName() sales ticketNums--,and remainsticketNums);}} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();// 解锁}}
}Lock与synchronized的区别
1、synchronized内置的Java关键字而Lock是一个Java类
2、synchronized无法判断获取锁的状态Lock可以判断是否获取到了锁
3、synchronized会自动释放锁Lock必须手动释放锁如果不释放锁则会造成死锁
4、synchronized线程1获得锁线程2一直等待 Lock锁时线程2就不一定会等待下去
5、synchronized可重入锁不可中断的非公平锁不可更改Lock可重入锁可以判断锁默认非公平可以修改
6、synchronized适合锁少量的代码同步问题Lock锁适合锁大量的同步代码
锁是什么如何判断锁的是谁
看8锁现象
生产者和消费者问题 生产者消费者问题 synchronized版 package PC;/*** 线程之间的通信问题生产者和消费者问题 等待唤醒 通知唤醒* 线程交替执行 A B 操作同一个变量 num 0* A num1* B num-1*/
public class A {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) {throw new RuntimeException(e);}}},A).start();new Thread(()-{for (int i 0; i 10; i) {try {data.decrement();} catch (InterruptedException e) {throw new RuntimeException(e);}}},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();}
}这真的线程安全吗如果有四个线程同时跑呢 答不安全当四个线程在跑时则会出现意料之外的情况 为什么会产生这种情况 答造成这种现象的原因是虚假唤醒 什么是虚假唤醒 多线程环境下有多个线程执行了wait()方法需要其他线程执行notify()或者notifyAll()方法去唤醒它们假如多个线程都被唤醒了但是只有其中一部分是有用的唤醒操作其余的唤醒都是无用功对于不应该被唤醒的线程而言便是虚假唤醒。 比如仓库有货了才能出库突然仓库入库了一个货品这时所有的线程货车都被唤醒来执行出库操作实际上只有一个线程货车能执行出库操作其他线程都是虚假唤醒。 参考博客Java线程虚假唤醒是什么、如何避免_java 虚假唤醒_秃秃爱健身的博客-CSDN博客
很重要以下是个人感悟
其实当初笔者在此处还是很困惑的为什么虚假唤醒会造成线程同时运行而不顾if条件语句后来笔者意识到一个很重要的问题“wait()方法会使线程放弃锁”。也就是说当A线程拿到了同步锁之后进入if条件语句判断如果此时条件为true它会进入waiting状态并放弃同步锁因此C线程在这段时间有可能会乘虚而入抢在B线程或D线程将A线程唤醒前进入同步代码块同样进入if语句的waiting状态之后B线程或D线程完成其业务逻辑后执行notifyAll()方法就会将A线程与C线程同时唤醒然后两者都会执行业务逻辑导致一次减两次加与我们的预期我们的逻辑是加一次减一次不符。因此线程不安全。
以上解释只是个人猜想还未曾验证过比如将wait换成sleep抱着线程休眠是否会出现同样的问题 如何避免虚假唤醒 将if条件语句改为while循环语句
当使用if条件语句时如果线程在if条件语句中被wait中断退出当其重新进入回到它原本所在的位置后就会发现它已经进行过判断了接下来就算已经有线程抢先一步操作它也会义无反顾地往下走因为没有条件能够拦住它啦
而当我们使用while循环语句会发现以下是官方文档所给的推荐代码
synchronized (obj) { while (condition does not hold and timeout not exceeded) { long timeoutMillis ... ; // recompute timeout values int nanos ... ; obj.wait(timeoutMillis, nanos); } ... // Perform action appropriate to condition or timeout
} 如果线程在while循环中被wait中断退出当其重新进入回到它原本所在的位置后就会发现本次循环已经结束**接下来并不是执行后面的业务代码而是返回到while循环开头重新判断一次是否满足条件。**这样的操作就保证了即使在退出重进也会进行再一次的判断确保线程安全。
以下为示例中被修改的代码片段
public synchronized void increment() throws InterruptedException {// 等待// ***************** 此处的if被改为while **********************while (number ! 0){this.wait();}// 业务number;System.out.println(Thread.currentThread().getName()number);// 通知其他线程我加一完毕this.notifyAll();
}// -1操作
public synchronized void decrement() throws InterruptedException {// 等待// ***************** 此处的if被改为while **********************while (number 0){this.wait();}// 业务number--;System.out.println(Thread.currentThread().getName()number);// 通知其他线程我减一完毕this.notifyAll();
}生产者消费者问题 JUC版 对应于synchronizedJUC版本下Lock锁也有对应的唤醒与停止方法分别是condition接口下的signal()与await()
以下是官方文档的描述 示例代码如下
package PC;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class B {public static void main(String[] args) {Data2 data new Data2();new Thread(()-{for (int i 0; i 10; i) {try {data.increment();} catch (InterruptedException e) {throw new RuntimeException(e);}}},A).start();new Thread(()-{for (int i 0; i 10; i) {try {data.decrement();} catch (InterruptedException e) {throw new RuntimeException(e);}}},B).start();new Thread(()-{for (int i 0; i 10; i) {try {data.increment();} catch (InterruptedException e) {throw new RuntimeException(e);}}},C).start();new Thread(()-{for (int i 0; i 10; i) {try {data.decrement();} catch (InterruptedException e) {throw new RuntimeException(e);}}},D).start();}
}// 口诀等待业务通知
// 资源类
class Data2{private int number 0;Lock lock new ReentrantLock();// 1操作public void increment() throws InterruptedException {Condition condition lock.newCondition();/*** condition.await(); 等待* condition.signalAll(); 唤醒全部*/try {lock.lock();// 业务代码 // 等待while (number ! 0){condition.await();}// 业务number;// 通知其他线程我加一完毕System.out.println(Thread.currentThread().getName()number);// } catch (Exception e) {throw new RuntimeException(e);}finally{lock.unlock();}}// -1操作public void decrement() throws InterruptedException {Condition condition lock.newCondition();try {lock.lock();// 业务代码 // 等待while (number 0){}// 业务number--;// 通知其他线程我减一完毕System.out.println(Thread.currentThread().getName()number);// } catch (Exception e) {throw new RuntimeException(e); } finally {lock.unlock();}}
}Condition 的优势在哪里 可以实现精准的通知和唤醒线程
以下示例实现精准唤醒线程在A线程执行完后精准唤醒B线程执行B线程执行完后精准唤醒C线程执行C线程执行完后精准唤醒A线程执行
package PC;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** A 执行完调用 BB 执行完调用 CC 执行调用 A*/
public class C {public static void main(String[] args) {// new 资源类Data3 data new Data3();// 创建线程并执行线程new Thread(()-{for (int i 0; i 5; i) data.printA();},A).start();new Thread(()-{for (int i 0; i 5; i) data.printB();},B).start();new Thread(()-{for (int i 0; i 5; i) data.printC();},C).start();}
}//资源类
class Data3{private Lock lock new ReentrantLock();private Condition conditionA lock.newCondition();private Condition conditionB lock.newCondition();private Condition conditionC lock.newCondition();private int number 1; // 若number1则A执行若number2则B执行若number3则C执行public void printA(){lock.lock();try {// 业务 判断-执行-通知while(number ! 1){conditionA.await();}System.out.println(Thread.currentThread().getName() now is AAAAA time!);// 唤醒指定的线程Bnumber ;conditionB.signal();} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();}}public void printB(){lock.lock();try {while(number ! 2){conditionB.await();}System.out.println(Thread.currentThread().getName()now is BBBBB time!);// 唤醒指定线程Cnumber;conditionC.signal();} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();}}public void printC(){lock.lock();try {while(number ! 3){conditionC.await();}System.out.println(Thread.currentThread().getName()now is CCCCC time!);number 1;conditionA.signal();} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();}}}总结
Lock锁与synchronized锁的区别要分清在多线程情况下不要使用if条件语句来判断是否wait或await某一线程要用while循环语句来判断否则线程不安全Lock锁下使用await与signal等价于wait和notify但有所区别Lock锁更加灵活和可以精准唤醒某些线程