JUC并发编程
学习记录狂神视频JUC
1.获取线程数
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-18 22:03
*/
public class Test01 {
public static void main(String[] args) {
//获取cpu的核数
//cpu 密集型 ,io密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
-
进程:一个程序,QQ.exe Music.exe 程序的集合; 一个进程往往可以包含多个线程,至少包含一个!
-
Java默认有几个线程? 2 个 mian、
-
GC 线程:开了一个进程
-
对于Java而言:Thread、Runnable、Callable Java 真的可以开启线程吗? 开不了
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
// 本地方法,底层的C++ ,Java 无法直接操作硬件
private native void start0();
1. 并发编程
-
并发、并行 并发(多线程操作同一个资源) CPU 一核 ,模拟出来多条线程,快速交替
-
并行(多个人一起行走) CPU 多核 ,多个线程可以同时执行;
并发编程的本质:充分利用CPU的资源
线程有几个状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待,死死地等
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
wait/sleep 区别
1、来自不同的类 wait => Object sleep => Thread
2、关于锁的释放 wait 会释放锁,sleep 睡觉了,抱着锁睡觉,不会释放!
3、使用的范围是不同的 wait ->wait必须在同步
4、是否需要捕获异常 wait 不需要捕获异常 sleep 必须要捕获异常
Locke锁
1.传统加锁方式synchronized
package com.xuda.lock;
/**
* @author :程序员徐大大
* @description:买票
* @date :2022-04-18 22:08
*/
/**
* 真正的多线程开发,
* 线程就是一个单独的资源类,没有任何附属的操作
* 1.属性 2.方法
*/
public class Test02 {
public static void main(String[] args) {
//并发:多线程操作同一个资源,吧资源丢入线程
Ticket ticket = new Ticket();
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();
}
}
//资源类 oop
class Ticket {
//属性 、方法
private int number = 30;
//买票方法
// synchronized 本质: 队列,锁
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
}
}
}
2.ReenteantLock锁
Lock三部曲
(1).new ReentrantLock();
(2).lock.lock(); // 加锁
(3).finally=>lock.unlock(); //解锁
package com.xuda.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author :程序员徐大大
* @description:Lock锁
* @date :2022-04-19 17:39
*/
//ReentrantLock :可重用锁
// ReentrantReadWriteLock.ReadLock :读锁
// ReentrantReadWriteLock.WriteLock :写锁
/**公平锁
* public ReentrantLock() {
* sync = new NonfairSync();
* }
*非公平锁
* public ReentrantLock(boolean fair){
* sync=fair?new FairSync():new NonfairSync();
* }
*/
public class SaleTicketDemo02 {
//公平锁:十分公平:可以先来后到
//非公平锁:十分不公平:可以插队 (默认)
public static void main(String[] args) {
//并发:多线程操作同一个资源类,吧资源丢入线程
Ticket2 ticket2 = new Ticket2();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket2.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
ticket2.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
ticket2.sale();
}
},"C").start();
}
}
// Lock三部曲
// 1、 new ReentrantLock();
// 2、 lock.lock(); // 加锁
// 3、 finally=> lock.unlock(); // 解锁
class Ticket2 {
//属性
// 方法
private int number = 30;
Lock lock = new ReentrantLock();
public void sale() {
lock.lock();//加锁
try {
//业务代码
if (number > 0) {
System.out.println(Thread.currentThread().getName()+ "卖出了" + (number--) + "票,剩余:"+number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); //解锁
}
}
}
1、Synchronized 内置的Java关键字, Lock 是一个Java类
2、Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
3、Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
4、Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下 去;
5、Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以 自己设置);
6、Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
生产者和消费者
:单例模式、排序算法、生产者和消费者、死锁
生产者和消费者问题 Synchronized 版
package com.xuda.lock;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-19 18:22
*/
public class dieLock {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
//判断等待、业务、通知
class Data { //资源
private int number = 0;
public synchronized void increment() throws InterruptedException {
if (number != 0) {
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "==>" + number);
// 通知其他线程,我+1完成了
this.notifyAll(); //唤醒其他所有线程
}
public synchronized void decrement() throws InterruptedException {
if (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程,我-1完毕
this.notifyAll();
}
}
上面会产生虚假唤醒
if 改为 while 判断
package com.xuda.lock;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-19 18:22
*/
public class dieLock {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//判断等待、业务、通知
class Data { //资源
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0) {
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "==>" + number);
// 通知其他线程,我+1完成了
this.notifyAll(); //唤醒其他所有线程
}
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程,我-1完毕
this.notifyAll();
}
}
JUC版的生产者和消费者问题
通过Lock 找到 Condition
package com.xuda.lock;
import java.util.Date;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-19 19:17
*/
public class LockIn {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data2.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data2.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data2.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data2.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//判断等待、业务、通知
class Data2 { //资源
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await(); // 等待
//condition.signalAll(); // 唤醒全部
public void increment() throws InterruptedException {
lock.lock(); //加锁
try {
while (number != 0) {
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "==>" + number);
// 通知其他线程,我+1完成了
condition.signalAll(); //唤醒其他所有线程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//解锁
}
}
public void decrement() throws InterruptedException {
lock.lock(); //加锁
try {
while (number == 0) {
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
//通知其他线程,我-1完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
狂神:任何一个的新技术,绝对不是仅仅只是覆盖了原来的技术
优势和补充 Condition精准的通知和唤醒
package com.xuda.lock;
import java.util.Date;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-19 19:17
*/
public class LockIn {
/**
* A 执行完调用B,B执行完调用C,C执行完调用A
*/
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data2.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data2.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data2.printC();
}
},"C").start();
}
}
//判断等待、业务、通知
class Data2{
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1; // 1A 2B 3C
public void printA(){
lock.lock();
try {
// 业务,判断-> 执行-> 通知
while (number!=1){
// 等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAAAAA");
// 唤醒,唤醒指定的人,B
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
// 业务,判断-> 执行-> 通知
while (number!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
// 唤醒,唤醒指定的人,c
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
// 业务,判断-> 执行-> 通知
// 业务,判断-> 执行-> 通知
while (number!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
// 唤醒,唤醒指定的人,c
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
八锁现象
如何判断锁的是谁!永远的知道什么锁,锁到底锁的是谁!
深刻理解我们的锁
8锁,就是关于锁的8个问题 *
1.synchronized的锁
package com.xuda.eitlock;
import java.util.concurrent.TimeUnit;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-19 20:15
*/
public class CallTest {
/**
* 8锁,就是关于锁的8个问题
* 1、标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
* 1、sendSms延迟4秒,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
*/
public static void main(String[] args) {
Phone phone1 = new Phone();
//锁的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
new Thread(()->{
phone1.call();
},"A").start();
}
}
class Phone {
// 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("打电话");
}
}
synchronized锁的方法
2.synchronized锁的是方法不是类
package com.xuda.eitlock;
import java.util.concurrent.TimeUnit;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-19 20:28
*/
public class CallTest2 {
public static void main(String[] args) {
Phone2 phone2 = new Phone2();
//锁的存在
new Thread(()->{
phone2.sendSms();
},"A").start();
new Thread(()->{
phone2.call();
},"A").start();
new Thread(()->{
phone2.hello();
},"A").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("打电话");
}
public void hello() {
System.out.println("hello");
}
}
2.加入static后static 静态方法类一加载就有了!所以synchronized锁的是Class
package com.xuda.eitlock;
import java.util.concurrent.TimeUnit;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-19 20:32
*/
public class CallTest3 {
public static void main(String[] args) {
// 两个对象的Class类模板只有一个,static,锁的是Class
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
//锁的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone3{
// synchronized 锁的对象是方法的调用者!
// static 静态方法
// 类一加载就有了!锁的是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("打电话");
}
}
3.静态的同步方法 锁的是 Class 类模板,普通的同步方法 锁的调用者
package com.xuda.eitlock;
import java.util.concurrent.TimeUnit;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-19 20:32
*/
public class CallTest3 {
public static void main(String[] args) {
// 两个对象的Class类模板只有一个,static,锁的是Class
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
//锁的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone3{
// 静态的同步方法 锁的是 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("打电话");
}
}
new this 具体的一个实体类
static Class 唯一的一个模板
集合类不安全
1.List不安全使用CopyOnWriteArrayList
package com.xuda.list;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author :程序员徐大大
* @description:TODO
* @date :2022-04-19 20:42
*/
// java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {
public static void main(String[] args) {
//并发下 ArrayList不安全
/**
* 解决方案;
* 1、List<String> list = new Vector<>();
* 2、List<String> list = Collections.synchronizedList(new ArrayList<>
());
* 3、List<String> list = new CopyOnWriteArrayList<>();
*/
// CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略;
// 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
// 在写入的时候避免覆盖,造成数据问题!
// 读写分离
// CopyOnWriteArrayList 比 Vector 厉害在哪里?
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
}).start();
}
}
}
2.Set不安全,使用CopyOnWriteArraySet
package com.xuda.list;
/**
* @author :程序员徐大大
* @description:Set不安全
* @date :2022-04-19 20:46
*/
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* 同理可证 : ConcurrentModificationException
* //1、Set<String> set = Collections.synchronizedSet(new HashSet<>());、
*/
public class Set {
// Set<String> set = new HashSet<>();
// Set<String> set = Collections.synchronizedSet(new HashSet<>());
public static void main(String[] args) {
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
}).start();
}
}
}
- hashSet 底层是什么?
public HashSet() {
map = new HashMap<>();
}
// add set 本质就是 map key是无法重复的!
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object(); // 不变得值!
- Map 不安全
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
// ConcurrentModificationException
public class MapTest {
public static void main(String[] args) {
// map 是这样用的吗? 不是,工作中不用 HashMap
// 默认等价于什么? new HashMap<>(16,0.75);
// Map<String, String> map = new HashMap<>();
// 唯一的一个家庭作业:研究ConcurrentHashMap的原理
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <=30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().subst ring(
0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
5.Callable
1、可以有返回值
2、可以抛出异常
3、方法不同,run()/ call()
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.ReentrantLock;
/**
* 1、探究原理
* 2、觉自己会用
*/
public class CallableTest {
public static void main(String[] args) throws ExecutionException,
InterruptedException {
// new Thread(new Runnable()).start();
// new Thread(new FutureTask<V>()).start();
// new Thread(new FutureTask<V>( Callable )).start();
new Thread().start(); // 怎么启动Callable
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread); // 适配类
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() {
System.out.println("call()"); // 会打印几个call
// 耗时的操作
return 1024;
}
细节: 1、有缓存 2、结果可能需要等待,会阻塞!