目录标题
1. 多线程基础



线程有几个状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
公平锁:先来的先执行
非公平锁:十分不公平,可以插队(默认)
2. Synchronized和Lock
Synchronized
package com.xiaofan;
public class SaleTicketDemo01 {
public static void main(String[] args) {
// 并发: 多线程操作同一个资源类, 把资源类丢入线程
final Ticket ticket = new Ticket();
// @FunctionalInterface 函数式接口, jdk1.8 lambda 表达式(参数)->{代码}
new Thread(()->{ for (int i = 0; i < 31; i++) ticket.sale(); }, "A").start();
new Thread(()->{ for (int i = 0; i < 31; i++) ticket.sale(); }, "B").start();
new Thread(()->{ for (int i = 0; i < 31; i++) ticket.sale(); }, "C").start();
}
}
// 资源类 OOP
class Ticket {
// 属性,方法
private int number = 30;
// 卖票的方式
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "张票, 剩余票数:" + number);
}
}
}
Lock
package com.xiaofan;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo02 {
public static void main(String[] args) {
// 并发: 多线程操作同一个资源类, 把资源类丢入线程
final Ticket2 ticket2 = new Ticket2();
// @FunctionalInterface 函数式接口, jdk1.8 lambda 表达式(参数)->{代码}
new Thread(()->{ for (int i = 0; i < 31; i++) ticket2.sale(); }, "A").start();
new Thread(()->{ for (int i = 0; i < 31; i++) ticket2.sale(); }, "B").start();
new Thread(()->{ for (int i = 0; i < 31; i++) ticket2.sale(); }, "C").start();
}
}
// Lock三部曲
// 1. new ReentrantLock();
// 2. lock.lock();
// 3. lock.unlock(); 解锁
class Ticket2 {
// 属性,方法
private int number = 30;
private 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();
}
}
}
Synchronized 和 Lock的区别
- Synchronized 内置的Java关键字, Lock是一个Java类
- Synchronized 无法判断获取锁的状态, Lock可以判断是否获取到了锁
- Synchronized 会自动释放锁, Lock必须要手动释放锁,如果不释放锁,死锁!
- Synchronized 线程1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去
- Synchronized 可重入锁,不可以中断的,非公平锁;Lock,可重入锁,可以判断锁,非公平锁(可自己设置)
- Synchronized 适合锁少量的代码同步问题, Lock适合锁大量的同步代码!

3. 生产者和消费者问题
生产者和消费者问题Synchronized版
面试手写的: 单例模式、排序算法、生产者和消费者、死锁
package com.xiaofan.pc;
/**
* 线程之间的通信问题: 生产者和消费者问题! 等待环形, 通知唤醒
* 线程交替执行 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(()->{
try {
for (int i = 0; i < 10; i++) data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) 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);
// 通知其他线程, 我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if (number == 0) {
this.wait();
}
number --;
System.out.println(Thread.currentThread().getName() + " => " + number);
// 通知其他线程, 我+1完毕了
this.notifyAll();
}
}

package com.xiaofan.pc;
/**
* 线程之间的通信问题: 生产者和消费者问题! 等待环形, 通知唤醒
* 线程交替执行 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(()->{
try {
for (int i = 0; i < 10; i++) data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "B").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "C").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) data.increment();
} 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);
// 通知其他线程, 我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
this.wait();
}
number --;
System.out.println(Thread.currentThread().getName() + " => " + number);
// 通知其他线程, 我+1完毕了
this.notifyAll();
}
}
JUC版本的生产者和消费者
package com.xiaofan.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程之间的通信问题: 生产者和消费者问题! 等待环形, 通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num + 1
* B num - 1
*/
public class B {
public static void main(String[] args) {
Data1 data1 = new Data1();
new Thread(()->{ for (int i = 0; i < 10; i++) data1.increment(); }, "A").start();
new Thread(()->{ for (int i = 0; i < 10; i++) data1.decrement(); }, "B").start();
new Thread(()->{ for (int i = 0; i < 10; i++) data1.increment(); }, "C").start();
new Thread(()->{ for (int i = 0; i < 10; i++) data1.decrement(); }, "D").start();
}
}
// 判断等待, 业务, 通知
class Data1 {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//+1
public void increment() {
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();
}
}
//-1
public void decrement() {
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.xiaofan.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 多线程轮流打印 A-B-C
*/
public class C {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{ for (int i = 0; i < 10; i++) data3.printA();}, "线程1").start();
new Thread(()->{ for (int i = 0; i < 10; i++) data3.printB();}, "线程2").start();
new Thread(()->{ for (int i = 0; i < 10; i++) data3.printC();}, "线程3").start();
}
}
class Data3 {
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();
}
number = 2;
System.out.println(Thread.currentThread().getName() + "=> A...");
// 唤醒指定的人
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
// 业务-判断-执行-通知
while(number != 2) {
condition2.await();
}
number = 3;
System.out.println(Thread.currentThread().getName() + "=> B...");
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
// 业务-判断-执行-通知
while(number != 3) {
condition3.await();
}
number = 1;
System.out.println(Thread.currentThread().getName() + "=> C...");
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
4. 8锁问题
如何判断锁的是谁!对象 Class
- 总结
- new this 具体的一个对象
- static Class 唯一的一个模板
八锁问题
核心就是锁的只会是调用实体,只要是不同对象,对象和静态类模板就不会形成锁关系
5. 集合类不安全
5.1. List不安全
package com.xiaofan.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
// java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {
public static void main(String[] args) {
// 单线程安全
/*List<String> list = Arrays.asList("1", "2", "3");
list.forEach(System.out::println);*/
// 并发下ArrayList不安全的吗?synchronized:
/**
* 解决方案:
* 1. List<String> list = new Vector<>(); Vector是1.0出来的,List是1.2出来的
* 2. List<String> list = Collections.synchronizedList(new ArrayList<>());
* 3. List<String> list = new CopyOnWriteArrayList<>();
*/
// CopyOnWrite 写入时复制 COW 计算机程序设计的一种优化策略
// 多个线程调用的时候,list读取的时候,固定的,写入(覆盖), 在写入的时候避免覆盖,造成数据问题!读写分离
// CopyOnWriteArrayList 比 Vector牛逼的地方是底层应用了Lock锁,而Vector应用了synchronized
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 20; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
5.2. Set不安全
package com.xiaofan.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
// 同理:java.util.ConcurrentModificationException 并发修改异常!
public class SetTest {
public static void main(String[] args) {
/**
* 解决方案:
* 1. Set<String>set = Collections.synchronizedSet(new HashSet<>());
* 2. Set<String>set = new CopyOnWriteArraySet<>();
*/
Set<String>set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 20; 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<>();
}
// add set 本质就是map的key是无法重复的!
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// 不变的值
private static final Object PRESENT = new Object();
5.3. Map不安全
package com.xiaofan.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
// java.util.ConcurrentModificationException 并发修改异常
public class MapTest {
public static void main(String[] args) {
/**
* 解决方案:
* 1. Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
* 2. Map<String, String> map = new ConcurrentHashMap<>();
*/
Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
for (int i = 1; i <= 10; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
}).start();
}
}
}
本文探讨了Java中的多线程基础,包括线程状态,以及Synchronized和Lock两种锁机制的使用和区别。通过生产者消费者问题的实现,展示了Synchronized和基于Condition的Lock如何实现线程间的通信。同时,文章指出了ArrayList、HashSet和HashMap在并发场景下的不安全性,并提供了CopyOnWriteArrayList和ConcurrentHashMap等线程安全的集合类解决方案。
1045

被折叠的 条评论
为什么被折叠?



