1、什么是JUC
-
官方文档+源码
面试高频问
java.util
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
java,util 工具包、包、分类
业务:普通的线程代码 Thread
Runnable 没有返回值
2、线程和进程
线程和进程 如果不能用一句话说出来的技术就是不扎实
进程:就是一个程序 一个程序的集合
一个进程往往可以有多个线程至少包含一个
java 默认高就有两个线程 main 、GC:垃圾回收
线程:开了一个java 有main线程 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();
并发、并行
并发编程:并发 、并行
并发 (多线程操作同一个资源) 充分利用cpu的资源
- CPU 一核 模拟出来多条线程,天下武功为快不破 快速交替模拟并行(宏观上并行微观上窜行)
并行 (多个人一起走)
-
CPU多核 多个线程可以同时进行;线程池
package com.lmq; /** * @author 羡鱼 * @version 1.0 * @date 2023/7/29 17:05 */ public class JucTest { public static void main(String[] args) { // 获取cpu的核数 System.out.println(Runtime.getRuntime().availableProcessors()); } }
所有公司都看中
线程有几个状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待 一直等
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
wait/sleep 区别
1、来自不同的类
wait =>Object
sleep => Thread
2、关于锁的释放
wait 会释放锁 sleep睡觉了,抱着锁睡觉了,不会释放!
3、使用范围是不同的
wait: 必须在同步代码块中使用
sleep 可以任何地方睡觉
4、是否要捕获异常
wait 要捕获异常
sleep 必须捕获异常
3、Lock锁(重点)
传统Synchronized 和 Lock 区别
1、 Synchronized 内置的JAVA关键字 Lock 是一个java类
2、 Synchronized 无法判断获取锁的状态Lock可以
3、 Synchronized 会自动释放锁Lock 必须手动释放锁 ,不释放锁就死锁
4、 Synchronized 线程1(获得锁,阻塞了) 线程2(一直等待);Lock 锁就不一定等下去
5、 Synchronized 可重入锁不可中断,非公平;Lock 可以重锁,可以判断 锁, 非公平(可以自己设置);
6、 Synchronized 适合锁少量的同步代码, Lock 适合锁大量的同步代码
// synchronized 本质 : 队列 锁
Synchronized wait
公平锁 先来后到
非公平锁 可以插队(默认)
锁是什么 如何判断锁的是谁
4、生产者和消费者的问题
面试常问
单例模式 排序算法 生产者消费者 死锁
生产者消费者
package com.lmq.pc;
/**
* @author 羡鱼
* @version 1.0
* @date 2023/7/29 18:12
* <p>
* 线程之间的通讯 生产者 消费者 等待幻想 通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num=1
* 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;
public synchronized void increment() throws InterruptedException {
if (number != 0) {
this.wait();
}
number++;
// 通知其他线程
this.notify();
System.out.println(Thread.currentThread().getName() + "=>" + number);
}
public synchronized void decrement() throws InterruptedException {
if (number == 0) {
this.wait();
}
number--;
this.notify();
System.out.println(Thread.currentThread().getName() + "=>" + number);
}
}
使用if判断会出现虚假唤醒 使用while
package com.lmq.pc;
/**
* @author 羡鱼
* @version 1.0
* @date 2023/7/29 18:12
* <p>
* 线程之间的通讯 生产者 消费者 等待幻想 通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num=1
* B num-1
*/
public class A {
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) {
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.decrement();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, "C").start();
}
}
// 判断 等待 业务 通知
class Data {
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0) {
this.wait();
}
number++;
// 通知其他线程
this.notify();
System.out.println(Thread.currentThread().getName() + "=>" + number);
}
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
this.wait();
}
number--;
this.notify();
System.out.println(Thread.currentThread().getName() + "=>" + number);
}
}
原来的 synchronized wait notify
juc的 (Lock)Lock ()await signal
juc 版本生产者消费者
Lock 找到 Condi
上锁 等待 通知 解锁
package com.lmq.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author 羡鱼
* @version 1.0
* @date 2023/7/29 18:48
*/
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 (Exception 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 (Exception e) {
throw new RuntimeException(e);
}
}
}, "D").start();
}
}
class Data2 {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
condition.await();
}
number++;
// 通知其他线程
condition.signalAll();
System.out.println(Thread.currentThread().getName() + "=>" + number);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
while (number == 0) {
condition.await();
}
number--;
condition.signalAll();
System.out.println(Thread.currentThread().getName() + "=>" + number);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Lock 可以精准唤醒进程
可以设置多个监视器
5、8锁现象
如何判断锁的是谁!
- s锁就是关于锁的8个问题
-
- 标准情况下 连个线程先打印 发短信还是打电话 1、发短信 2、打电话 都是phone 调用所以先发短信 后 打电话
-
- 发短信方法延迟四秒两个线程先打印? 发短信还是先
-
- 加入普通方法后先发短信还是普通方法? 执行普通方法
-
- 两个对象 两个同步方法 先发短信还是先打电话? 先打电话 对象不同 phone2 和phone1 各有一把锁
-
- 增加两个静态方法,只有一个对象,先打印 发短信 还是打电话? 发短信static 锁的是类 全局唯一
-
- 两个对象!增加两个静态的同步方法 先打印发短信还是打电话? 发短信 static 锁的是class class只有一个
-
- 一个是静态同步方法一个是普通同步方法, 一个一下先发育发短信还是打电话? 打电话
-
- 一个是静态同步方法一个是普通同步方法
package com.lmq.lock8;
import java.util.concurrent.TimeUnit;
/**
* @author 羡鱼
* @version 1.0
* @date 2023/7/29 20:28
*
*
*
* s锁就是关于锁的8个问题
* 1. 标准情况下 连个线程先打印 发短信还是打电话 1、发短信 2、打电话 都是phone 调用所以先发短信 后 打电话
* 2. 发短信方法延迟四秒两个线程先打印? 发短信还是先
* 3. 加入普通方法后先发短信还是普通方法? 执行普通方法
* 4. 两个对象 两个同步方法 先发短信还是先打电话? 先打电话 对象不同 phone2 和phone1 各有一把锁
* 5. 增加两个静态方法,只有一个对象,先打印 发短信 还是打电话? 发短信static 锁的是类 全局唯一
* 6. 两个对象!增加两个静态的同步方法 先打印发短信还是打电话? 发短信 static 锁的是class class只有一个
* 7. 一个是静态同步方法一个是普通同步方法, 一个一下先发育发短信还是打电话? 打电话
* 8. 一个是静态同步方法一个是普通同步方法
*
*/
public class Text1 {
public static void main(String[] args) throws InterruptedException {
Phone phone1 = new Phone();
Phone phone2 = new Phone();
new Thread(phone1::sendSms,"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(phone2::call,"B").start();
}
}
class Phone{
// synchronized 锁的对象是方法的调用者(在上面可以看到锁的phone phone调用的方法)! 两个方法用的同一个锁 谁先拿到谁先执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("sendSms");
}
public synchronized void call(){
System.out.println("call");
}
public void hello() {
System.out.println("hello");
}
}
小结
new this 具体的一个手机
static Class 唯一的一个模版
6、集合类不安全
并发下 集合就不安全
package com.lmq.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author 羡鱼
* @version 1.0
* @date 2023/7/29 21:13
*/
// java.util.ConcurrentModificationException
public class ListTest1 {
public static void main(String[] args) {
// 并发下 ArrayList 不安全
// 解决方案
// 1. 在ArrayList 前面就已经有 Vector() 在java 1.0 就有了
// 2. Collections.synchronizedList(new ArrayList<>())
// 3. new CopyOnWriteArrayList<>(); JUC方案
// CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略
// 多线程调用的时候 List 读取的时候 固定的写入(覆盖)
// 在写入的时候避免覆盖 造成数据问题
// 在写入的时候避免覆盖 造成数据问题
// 读写分离
// CopyOnWriteArrayList<>() 比 Vector() 强在哪里 只要用synchronized效率就比lock锁底
// ArrayList<String> list = new ArrayList<>();
// List<String> list = Collections.synchronizedList(new ArrayList<>()) ;
List<String> list = new CopyOnWriteArrayList<>();
for (int i=0;i<=10;i++){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
学习方法推荐 1.先学会用 2.货比三家寻找解决方案 3.分析源码
Set 方法不安全
package com.lmq.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @author 羡鱼
* @version 1.0
* @date 2023/7/30 10:14
*/
public class SetTest {
public static void main(String[] args) {
// HashSet<String> set = new HashSet<>();
// java.util.ConcurrentModificationException
// 方案一 Set<Object> set = Collections.synchronizedSet(new HashSet<>());
// 方案二 Set<String> set = new CopyOnWriteArraySet<>();
Set<String> set = new CopyOnWriteArraySet<>();
for (int i =1;i<=10;i++){
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
hashSet 底层是什么?
就是HashMap
7、Map 不安全
package com.lmq.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author 羡鱼
* @version 1.0
* @date 2023/7/30 10:50
*/
public class MapTest {
public static void main(String[] args) {
// 什么是map 是怎么用的 等价于什么
// 不用 工作中不用HashMap
// 默认等价于什么 new HashMap<>(16,0.75)
// Map<String, String> map = new HashMap<>();
// Map map = Collections.synchronizedMap(new HashMap<String,String>());
Map<String, String> map = new ConcurrentHashMap<>();
// 加载因子 初始化容量
for (int i = 0; i <= 30; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
}
7、 Callable (简单)
Thread 只能接受 Runable
- new thread(new Runnable()).start();
- new Thread(new FutureTask
()).start(); - 两个对等 FutureTask是Runnable的实现类
- new Thread(new FutureTask
()).start();
package com.lmq.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author 羡鱼
* @version 1.0
* @date 2023/7/30 11:38
*/
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new thread(new Runnable()).start();
// new Thread(new FutureTask<V>()).start();
// 两个对等 FutureTask是Runnable的实现类
// new Thread(new FutureTask<V>()).start();
new Thread().start();
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start(); // 细节 结果有缓存 只打印一个hello 结果可能需要等待 会阻塞
Object o = futureTask.get(); // 获取Callable 返回值
System.out.println(o);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("hello");
return 1024;
}
}
标签:java,Thread,util,线程,new,多线程,public
From: https://www.cnblogs.com/lmq886/p/17591370.html