1.什么是JUC
JAVA Util Concurrent
2.线程和进程
进程是程序的集合,一个进程至少包含一个线程
java默认有两个线程 gc 和main
java真的可以开启线程吗?答案是否定的
//Thread.start()源码
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 */
}
}
}
//调用的是本地方法,java无法直接操作硬件!!
private native void start0();
并发、并行
-
并发(多线程同时执行一个资源)
CPU一核,线程快速交替
-
并行(多人行走)
CPU多核,多个线程同时执行;线程池
//查看最处理器个数 System.out.println(Runtime.getRuntime().availableProcessors());
并发编程的本质:充分利用cpu的资源
线程有六个状态:new、runnable、blocked、waiting、timed_waiting、terminated
//创建
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
wait、sleep的区别
-
来自不同的类
wait —>Objec
sleep---->Thread
TimeUnit.SECONDS.sleep(1000);
-
关于锁的释放
wait:会释放对象锁
sleep:不会释放对象锁
-
使用的范围不同
wait:必须在同步代码块使用
sleep:在哪里都可以使用
-
是否需要捕获异常
wait:也会抛异常
sleep:一定会抛异常:InterruptedException
3.Lock锁
传统锁 synchronized
Lock 接口
1、 Lock lock = new ReentrantLock();
2、 lock.lock();
try {
//业务代码
}catch (Exception e){
e.printStackTrace();
}finally {
3、 lock.unlock();
}
synchronized和Lock的区别
- synchronized是关键字,Lock是接口。
- synchronized会自动释放锁,Lock必须手动释放锁,否则会产生死锁。
- synchronized适合锁住少量的同步代码,Lock适合锁住大量的代码。
- synchronized是非公平锁,Lock可以自主设置是公平锁还是非公平锁。
- synchronized无法获取锁的状态,Lock可以判断是否获取到了锁。
- synchronized 如果线程1获取锁进入阻塞状态,线程2会一直等待,Lock会尝试会尝试再次获取锁
锁是什么?如何判断锁的是谁?
谁调用synchronized所修饰的方法,锁的就是谁!
4.生产者消费者问题
synchroniezd 版本的
private int num=0;
//生产者
public synchronized void product(){
//如果资源不为0,当前线程停止生产
if (num!=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName()+"==>"+num);
//通知其他线程我+1完毕了
this.notify();
}
//消费者
public synchronized void consumer(){
//如果资源为0,当前线程停止消费
if (num==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName()+"==>"+num);
//通知其他线程我-1完毕了
this.notify();
}
问题:多个线程会出现 虚假唤醒 :线程被唤醒而没有通知
原因:if 只判断一次
//判断等待、业务、通知
private int num=0;
//生产者
public synchronized void product() throws InterruptedException {
//如果资源不为0,当前线程停止生产
while (num!=0){
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"==>"+num);
//通知其他线程我+1完毕了
this.notifyAll();
}
//消费者
public synchronized void consumer() throws InterruptedException {
//如果资源为0,当前线程停止消费
while (num==0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"==>"+num);
//通知其他线程我-1完毕了
this.notifyAll();
}
Lock 版本的
private int a=0;
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (a!=0){
notFull.await();
}
a++;
System.out.println(Thread.currentThread().getName()+"-->"+a);
notEmpty.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (a==0){
notFull.await();
}
a--;
System.out.println(Thread.currentThread().getName()+"-->"+a);
notEmpty.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
synchronized wait notifyAll
Lock await signal
condition的优势 可以 精确线程唤醒某个线程
代码示例
class Aa{
private int a=1;
final Lock lock = new ReentrantLock();
final Condition condition1 = lock.newCondition();
final Condition condition2 = lock.newCondition();
final Condition condition3 = lock.newCondition();
//1A 2B 3C
public void a() throws InterruptedException {
lock.lock();
try {
while (a!=1){
//等待
condition1.await();
}
a = 2;
System.out.println(Thread.currentThread().getName()+"AAAAAA");
//唤醒condition2的锁
condition2.signal();
} finally {
lock.unlock();
}
}
public void b() throws InterruptedException {
lock.lock();
try {
while (a!=2){
//等待
condition2.await();
}
a = 3;
System.out.println(Thread.currentThread().getName()+"BBBBBBBB");
//唤醒condition3的锁
condition3.signal();
} finally {
lock.unlock();
}
}
public void c() throws InterruptedException {
lock.lock();
try {
while (a!=3){
//等待
condition3.await();
}
a = 1;
System.out.println(Thread.currentThread().getName()+"CCCCCCCC");
//唤醒condition1的锁
condition1.signal();
} finally {
lock.unlock();
}
}
}
5.8锁现象
如何判断锁的是谁?
synchronized锁的是方法的调用者
//如果是静态同步方法,synchronized锁的是Class
//如果是普通的同步方法,synchronized锁的是方法的调用者
public static synchronized void call(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打电话");
}
6.集合安全问题
- ArrayList —> ConcurrentModificationException (并发修改异常)
//解决办法:1.用List list = new Vector<>(); 不推荐,因为它的add方法被synchronized所修饰,效率低
//2. Collections.synchronizedList(list);
//3. JUC 包下的CopyOnWriteArrayList:写入时复制,避免数据被覆盖
- HashSet —> ConcurrentModificationException (并发修改异常) 解决方法如上
//1. Collections.synchronizedSet(set);
//2. JUC 包下的CopyOnWriteArraySet:写入时复制,避免数据被覆盖
HashSet的底层是什么?
public HashSet() {
map = new HashMap<>();
}
HashSet的add()方法
//add 的本质就是 map.put(key,..)
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
- HashMap —> ConcurrentModificationException (并发修改异常) 解决方法如上
ConcurrentHashMap<String,String> maps = new ConcurrentHashMap<>();
7.Callable接口
public class CallAble implements Callable<String> {
public static void main(String[] args) throws Exception {
CallAble callAble = new CallAble();
FutureTask futureTask = new FutureTask(callAble);
new Thread(futureTask,"a").start();//只打印一次
new Thread(futureTask,"b").start();
//get方法可能产生阻塞
String result = (String) futureTask.get();
System.out.println(result);
}
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
return "hello";
}
}
细节:1 . 有缓存
- get()会产生阻塞
8.辅助类
CountDownLatch:倒计时计数器
//等下面的线程都执行完毕后才会执行countDownLatch.await()后面的逻辑
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread((
)->{
countDownLatch.countDown(); //数量-1
System.out.println(Thread.currentThread().getName()+"执行完毕");
},String.valueOf(i)).start();
}
countDownLatch.await(); //等待计数器归零,继续执行下面的逻辑
System.out.println("close door");
CyclicBarrier 加法计数器
CyclicBarrier cyclicBarrier = 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 {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
作用:等待指定线程执行完毕后,再执行的线程
Semaphore 信号量
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <=6; i++) {
final int temp=i;
new Thread(()->{
try {
//从该信号量获取许可证,如果已经满了,阻止直到可用。
semaphore.acquire();
System.out.println(temp+"进来了");
TimeUnit.SECONDS.sleep(1);
System.out.println(temp+"离开了");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放许可证,将其返回到信号量。
semaphore.release();
}
},Thread.currentThread().getName()).start();
}
}
作用:多个共享资源互斥的使用!并发限流,控制最大线程数!
9.读写锁(ReentrantReadWriteLock)
/*
读锁 ---》 共享锁 多个线程可以同时占有
写锁 ---》 独占锁 一次只能被一个线程占用
读 读 可以共存
读 写 不可以共存
写 写 不可以共存
*/
public class ReadWriteLockTest {
private volatile Map<String,Object> map = new HashMap<>();
//读写锁 更加细粒度的控制
ReadWriteLock lock = new ReentrantReadWriteLock();
//写入操作
public void putMap(String key,Object value){
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName()+"准备写入");
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入ok");
} finally {
lock.writeLock().unlock();
}
}
//读取
public void getMap(String key){
try {
lock.readLock();
System.out.println(Thread.currentThread().getName()+"准备读取");
System.out.println(Thread.currentThread().getName()+map.get(key));
System.out.println(Thread.currentThread().getName()+"读取ok");
} finally {
lock.readLock().lock();
}
}
10.阻塞队列BolckingQueue
*什么时候用到BlockingQueue: 多线程并发处理 线程池
学会使用队列:添加 、移除
NoSuchElementException/IllegalStateException: Queue full
4 组API
方式 | 抛出异常 | 有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | queue.offer(“a”,2, TimeUnit.SECONDS); |
移除 | remove() | poll() | take() | queue.poll(2, TimeUnit.SECONDS); |
判断队列首 | element() | peek() | - | - |
//抛出异常
public static void arrayBlocking(){
queue.add("a");
queue.add("b");
queue.add("c");
//IllegalStateException: Queue full
System.out.println("======================");
queue.remove();
queue.remove();
queue.remove();
//NoSuchElementException
queue.remove();
}
/**
* 不抛异常 有返回值
* **/
public static void test2(){
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.offer("a");
queue.offer("b");
queue.offer("c");
// System.out.println(queue.offer("d"));
System.out.println("======================");
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
//null
System.out.println(queue.poll());
}
/**
* 阻塞等待
* */
public static void test3() throws InterruptedException {
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.put("a");
queue.put("b");
queue.put("c");
//队列已满,线程阻塞
queue.put("d");
System.out.println("======================");
queue.take();
queue.take();java
queue.take();
//队列为空,线程阻塞
queue.take();
}
/**
* 超时等待
* */
public static void test4() throws InterruptedException {
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.offer("a");
queue.offer("a");
queue.offer("a");
queue.offer("a",2, TimeUnit.SECONDS);
System.out.println("======================");
queue.poll();
queue.poll();
queue.poll();
queue.poll(2, TimeUnit.SECONDS);
}
11.线程池(重点)
线程池必会:三大方法、七大参数、四大拒绝策略
池化技术
程序的运行 本质:占用系统资源!优化系统资源的使用:池化技术
线程池、连接池、内存池、对象池…
池化技术:事先准备好资源,有人用来我这拿,用完放回池内。
线程池的好处:
-
降低资源消耗
-
调好响应速度
-
方便管理
线程服用 、控制最大并发数、管理线程
三大方法:
ExecutorService executorService = Executors.newSingleThreadExecutor(); ExecutorService executorService = Executors.newFixedThreadPool(5); ExecutorService executorService = Executors.newCachedThreadPool(); ExecutorService executorService2=Executors.newScheduledThreadPool(10); try { for (int i = 0; i < 100; i++) { executorService.execute(()->{ System.out.println(Thread.currentThread().getName()); }); } } finally { executorService.shutdown(); } }
七大参数
public ThreadPoolExecutor(int corePoolSize, //核心线程池大小 //如何定义线程池最大线程数? 1、cpu密集型:Runtime.getRuntime.availableProcessors(); 2.IO密集型:判断程序十分消耗IO的线程,设置为它的数量的2倍 int maximumPoolSize,//最大线程池数量 long keepAliveTime,//超过连接时间没人用,就是释放 TimeUnit unit,//超市单位 BlockingQueue<Runnable> workQueue,//阻塞队列 ThreadFactory threadFactory,//创建线程 不用动 RejectedExecutionHandler handler//拒绝策略) {
不要使用Executors创建线程池:
- FixedThreadPool 和 SingleThreadPool 允许最大的请求队列长度为Integer.Max_value 导致·OOM
- CachedThreadPool和ScheduledThreadPool 允许最大的线程数量为Integer.Max_value 导致·OOM
四种拒绝策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n2K9RsKh-1609254725171)(C:\Users\86199\AppData\Roaming\Typora\typora-user-images\image-20201210105934633.png)]
-
//抛出异常 new ThreadPoolExecutor.AbortPolicy()
-
//哪里来的去哪里 不会抛出异常 new ThreadPoolExecutor.CallerRunsPolicy());
-
//队列满了尝试和第一个线程竞争 不会抛出异常 new ThreadPoolExecutor.DiscardOldestPolicy());
-
//队列满了 丢掉任务 不会抛出异常 new ThreadPoolExecutor.DiscardPolicy());
小结和拓展
最大线程池数量如何设置?
如何定义线程池最大线程数?
1. cpu密集型:Runtime.getRuntime.availableProcessors();
2. IO密集型:判断程序十分消耗IO的线程,设置为它的数量的2倍
12.四大函数式接口(重点)
函数式接口:只有一个方法的接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//简化编程模型
//foreach(参数):消费式者函数式接口模型
- Function
-
Function function = new Function<String,String>() { @Override public String apply(String o) { return o; } }; System.out.println(function.apply("tt")); Function f = (str)->{return str;};
-
Predicate 断定式接口
Predicate<String> predicate = new Predicate<String>() { @Override public boolean test(String s) { return s.isEmpty(); } }; System.out.println(predicate.test("ss")); Predicate<String> predicates = s -> {return s.isEmpty();}; System.out.println(predicates.test(""));
-
Consumer 消费式接口:只有参数,没有返回值
public static void main(String[] args) { /*Consumer<String> consumer = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; consumer.accept("ss");*/ Consumer<String> consumers = s -> { System.out.println("s"); }; }
-
Supplier 生产式接口 没有参数,只有返回值
/*Supplier<String> stringSupplier = new Supplier<String>() { @Override public String get() { return "ss"; } }; System.out.println(stringSupplier.get());*/ Supplier<String> stringSuppliers = ()->{ return "sss"; }; System.out.println(stringSuppliers.get());
13.流式计算 Stream
User u1 = new User(1,"a",20);
User u2 = new User(2,"b",21);
User u3 = new User(3,"c",23);
User u4 = new User(4,"d",24);
User u5 = new User(5,"e",25);
List<User> users = Arrays.asList(u1,u2,u3,u4,u5);
users.stream()
.filter(user -> {return user.getId()%2==0;})
.filter(user -> {return user.getAge()>20;})
.map(user -> { return user.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1).forEach(System.out::println);
14.ForkJoin
什么是ForkJoin
ForkJoin在jdk1.7中,并行执行任务,提高效率。大数据量!
ForkJoin特点:工作窃取
这里面维护的都是双端队列
ForkJoin使用步骤:
-
ForkJoinPool forkJoinPool = new ForkJoinPool();
-
long res = forkJoinPool.invoke(RecursiveTask task);
-
计算类继承 RecursiveTask
/**
* 1.传统算法 2. ForkJoin 3,Stream流
*/
public class Demon extends RecursiveTask<Long> {
private long start;
private long end;
public Demon(long start,long end) {
this.start = start;
this.end=end;
}
//临界点 超过该值将任务拆分
private static long temp = 10000l;
public static void main(String[] args) {
// test1(); //683
Long before = System.currentTimeMillis();
Demon demon = new Demon(1,10_0000_0000);
ForkJoinPool forkJoinPool = new ForkJoinPool();
long res = forkJoinPool.invoke(demon);
Long end = System.currentTimeMillis();
System.out.println("sum="+res+"\t"+(end-before)); //331*/
//test3();//177
}
public static void test1(){
long sum = 0;
Long before = System.currentTimeMillis();
for (long i = 1; i <= 10_0000_0000; i++) {
sum +=i;
}
Long end = System.currentTimeMillis();
System.out.println("sum="+sum+"\t"+(end-before));
}
/**如何使用forkjoin?
* 1.ForkJoinPool forkJoin = newForkJoinPool();
* 2.forkJoin.execute(ForkJoinTask<?> task)
* 3.创建ForkJoinTask
*/
@SneakyThrows
@Override
protected Long compute() {
//超过该值 分支合并
if ((end-start)>temp){
long middle = (start+end)/2;
Demon task1 = new Demon(start,middle);
task1.fork();
Demon task2 = new Demon(middle+1,end);
task2.fork();
return task1.join()+task2.join();
}else {
long sum = 0;
for (long i = start; i <= end; i++) {
sum +=i;
}
return sum;
}
}
15.异步回调
//没有返回值的异步回调
CompletableFuture completableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("111111111");
completableFuture.get();//该方法会阻塞 直到上面的线程执行完毕
16.理解volatile
请你谈谈对volatile的理解
volatile是java中的关键字,是虚拟机提供的轻量级的同步机制。
-
保证可见性
private volatile static int num=0; public static void main(String[] args) throws InterruptedException { new Thread(()->{ while (num==0){ // System.out.println("----"+num); } }).start(); TimeUnit.SECONDS.sleep(3); num=1; System.out.println("===="+num); }
-
不保证原子性
原子性:不可分割,要么全部执行,要么全部不执行。
解决办法:synchronized,但是不建议,因为volatile是轻量级synchronized
public static void test(){ //num++不是原子性操作 num++; }
使用原子类解决该问题:
static volatile AtomicInteger atomicInteger = new AtomicInteger();
public static void test(){
atomicInteger.getAndIncrement();
}
-
禁止指令重排
你写的程序并不是按照你写的顺序执行的
源代码—>编译器优化重排—>指令并行也可能重排—>内存系统也可能会重排—>执行
volatile可以避免指令重排
内存屏障,CPU指令,作用:
什么是JMM
JAVA内存模型,是一种约定,并不存在
- 线程解锁前必须把共享变量立即刷新到主内存。
- 线程加锁前线程必须把主存中的罪行变量读取到自己的工作内存
- 加锁和解锁必须是一把锁
8种操作
这8种操作每一对都不能分割。
javap -c 字节码文件
jad -sjava 字节码文件
18.彻底玩转单例模式
饿汉式单例
public class Hunger {
private byte[]b = new byte[1024*1024];
private byte[]b = new byte[1024*1024];
private byte[]b = new byte[1024*1024];
private static final Hunger intance = new Hunger();
private Hunger(){};
public Hunger getInstance(){
return intance;
}
}
问题: 造成内存浪费
解决办法:懒汉式单例
懒汉式单例(DCL)
public class Lazy {
private volatile static Lazy lazy;
private Lazy(){
synchronized (Lazy.class){
if (lazy!=null){
throw new RuntimeException("不要试图通过反射破坏我");
}
}
}
public static Lazy getInstance(){
if (lazy==null){
synchronized (Lazy.class){
if (lazy==null){
lazy = new Lazy();
}
}
}
return lazy;
}
public static void main(String[] args) throws Exception {
//反射破坏单例模式
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
//使private 失效
declaredConstructor.setAccessible(true);
Lazy lazy1 = declaredConstructor.newInstance();
Lazy lazy11 = declaredConstructor.newInstance();
Lazy lazy2 = Lazy.getInstance();
System.out.println(lazy1.hashCode());
System.out.println(lazy11.hashCode());
//System.out.println(lazy1==lazy2); false
}
}
问题:1、普通的懒汉式单例在多线程的情况下不安全,会生成多个instance
2、由于 lazy = new Lazy();可能会发生指令重排,因此用volatile修饰instance
解决: 1、 双重检验锁
2、volatile修饰instance
单例不安全,因为有反射!
枚举的单例模式是不能被反射的!!!
javap -c A.class
jad -sjava A.class
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
//试图用反射破坏
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor( String.class,int.class);
//抛出异常
EnumSingle enumSingle = constructor.newInstance();
System.out.println(enumSingle==instance1);
}
}
19、深入理解CAS
什么是CAS ? CompareAndSwap
private static AtomicInteger atomicInteger = new AtomicInteger(200);
public static void main(String[] args) {
//如果是200 则更新为3
boolean f = atomicInteger.compareAndSet(200,3);
System.out.println(f);//true
System.out.println(atomicInteger);//3
atomicInteger.compareAndSet(200,4);
System.out.println(f);
System.out.println(atomicInteger);
}
//如果是期望的值,则更新为update
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
Unsafe类
内存操作,效率高
CAS:比较工作内存和主内存的值,如果这个值是期望的,则执行操作,如果不是一直循环!
ABA 问题:狸猫换太子
20.原子引用
AtomicInteger integer = new AtomicInteger(100);
//捣乱的线程
System.out.println(integer.compareAndSet(100,101));
System.out.println(integer.get());
System.out.println(integer.compareAndSet(101,100));
System.out.println(integer.get());
//期望的线程
System.out.println(integer.compareAndSet(100,101));
System.out.println(integer.get());
如何解决?原子引用
解决ABA问题:引入原子引用,类似乐观锁。带版本号的原子操作
public static void main(String[] args) throws InterruptedException {
/**
* 解决ABA问题的办法就是原子引用,类似乐观锁,通过加版本号 让A线程操作完成后让B线程知道,
*/
AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(100, 1);
new Thread(()->{
System.out.println(reference.compareAndSet(100, 101, reference.getStamp(), reference.getStamp() + 1));
//获取版本号+reference的值
System.out.println(reference.getStamp()+"\t"+reference.getReference());
},"A").start();
new Thread(()->{
System.out.println(reference.compareAndSet(101, 102, reference.getStamp(), reference.getStamp() + 1));
System.out.println(reference.getStamp()+"\t"+reference.getReference());
},"B").start();
}
21.各种锁的理解
21.1 公平锁、非公平锁
公平锁:不能插队,线程先来先执行
非公平锁:可以插队,3s 3h
Lock、Synchronized 默认都是非公平锁。
public ReentrantLock() {
sync = new NonfairSync();
}
21.2 可重入锁(递归锁)
synchronized版
/**
* synchronized版本的 可重入锁 A线程获取A锁后自动获取B锁,然后释放
*/
public class ReenLockTest {
public static void main(String[] args) {
new Thread(()->{
call();
},"A").start();
new Thread(()->{
call();
},"B").start();
}
public static synchronized void call(){
System.out.println(Thread.currentThread().getName()+"call");
message();
}
public static synchronized void message(){
System.out.println(Thread.currentThread().getName()+"message");
}
Lock版本
/**
* Lock版本的可重入锁
*/
public class ReenLockTest2 {
public static void main(String[] args) {
new Thread(()->{
call();
},"A").start();
new Thread(()->{
call();
},"B").start();
}
static Lock lock = new ReentrantLock();
public static void call(){
//加锁和解锁必须配对,否则会死锁
lock.lock();
//lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"call");
message();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void message(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"message");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
21.3 自旋锁
/**
* 通过CAS实现
*/
public class ZiXuan {
static AtomicReference reference = new AtomicReference<Thread>();
static Thread thread = Thread.currentThread();
//加锁
public static void mylock(){
System.out.println(Thread.currentThread().getName()+"加锁");
while (!reference.compareAndSet(null,thread)){
}
}
//解锁
public static void myunlock(){
System.out.println(Thread.currentThread().getName()+"解锁");
reference.compareAndSet(thread,null);
}
public static void main(String[] args) {
new Thread(()->{
try {
mylock();
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
myunlock();
}
},"A").start();
}
}
21.4 死锁
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 占有且等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不可强行占有:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
解决问题:
jps -l 查看进程
jstack 进程号