文章目录
一、虚假唤醒问题
- 因为wait在那里睡在哪里醒(如下在判断里面睡觉后,又在判断里面唤醒但是不满足判断条件也会被唤醒执行++操作)
if (num!=0){
this.wait();
}
num++;
- 改进
while (num!=0){
this.wait();
}
num++;
二、集合线程不安全
Exception in thread "1" Exception in thread "6" java.util.ConcurrentModificationException
HashSet解决方案
Set<String> set = new CopyOnWriteArraySet<>();
HashMap解决方案
Map<String,String> map = new ConcurrentHashMap<>();
ArrayList解决方案
-
使用Vector
List<String> list = new Vector<>();
-
使用Collections工具类
List<String> list = Collections.synchronizedList(new ArrayList<>());
-
写时复制
List<String> list = new CopyOnWriteArrayList<>();
三、多线程锁的使用
synchronized锁的范围
- 对于普通同步方法,锁的是当前实例对象
- 对于静态同步方法,锁的是当前的Class对象
- 对于同步方法块,锁的是synchronized括号里配置的对象
公平锁和不公平锁
//公平锁
private ReentrantLock lock = new ReentrantLock(true);
//非公平锁(默认情况下是非公平锁)
private ReentrantLock lock = new ReentrantLock(false);
可重入锁(递归锁)
-
可以再次进入同步区 synchronized(隐式) 和 Lock(显式) 都是可重入锁
-
举个例子 家里的大门的锁打开 家里其他区域就可以自由进入
//可重复调用 及证实可重入锁 public synchronized void add(){ add(); } //console Exception in thread "main" java.lang.StackOverflowError
死锁
-
两个或者两个以上进程在执行过程中,因为争夺资源,而造成一种相互等待的现象,如果没有外力作用的情况下,他们无法再执行下去
-
验证是否是死锁
-
jps 类似Linux ps -ef
-
jstack jvm自带堆栈跟踪工具(最后一行)
相互等待ing
-
Collable接口
Runnable | Callable | |
---|---|---|
返回值 | 无 | 有 |
异常 | 无 | 有 |
运行方法 | run | call |
-
实现Callable接口创建一个线程
new FutureTask<>(() -> 1024);
-
isDone() 判断是否结束
-
get()获取返回结果
辅助类
-
CountDomnLatch 减少计数 全部子线程执行完再去执行主线程
-
await 致使调用线程阻塞
-
countDown -1 当计数器变为0因await 方法而阻塞的线程会被重新唤醒
-
例子: 班级里同学都走了之后才可以锁门
public static void main(String[] args) throws InterruptedException { //创建一个辅助类CountDownLatch CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <= 6 ; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+"::号同学离开教室"); countDownLatch.countDown(); },String.valueOf(i)).start(); } countDownLatch.await(); System.out.println("锁门..."); }
-
-
CyclicBarrier
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DPx804Gz-1637851525641)(C:\Users\stone\AppData\Roaming\Typora\typora-user-images\image-20211123101824346.png)]
private static final int NUMBER = 7 ; public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, ()->System.out.println("集齐七个龙珠就可以召唤神龙")); for (int i = 1; i <= NUMBER ; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+"星龙珠被收集到"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },String.valueOf(i)).start(); } }
-
Semaphore 信号量的使用
//三个车位供六辆车使用 public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); for (int i = 1; i <= 6 ; i++) { new Thread(()->{ try { //抢占车位 阻塞 semaphore.acquire(); System.out.println(">>>>>"+Thread.currentThread().getName()+"停在停车位"); TimeUnit.SECONDS.sleep(new Random().nextInt(5)); System.out.println(Thread.currentThread().getName()+"离开停车位>>>>>"); } catch (InterruptedException e) { e.printStackTrace(); } finally { //车位可以供别的车使用 释放阻塞 semaphore.release(); } },String.valueOf(i)).start(); } }
读写锁
-
悲观锁
-
不支持并发操作
-
每一步操作 上锁 解锁
每次拿数据都认为别人会修改数据,所以要加锁,别人只能等待,直到我释放锁才能拿到锁;数据库的行锁、表锁、读锁、写锁都是这种方式,java中的synchronized和ReentrantLock也是悲观锁的思想。
-
-
乐观锁
-
使用版本号控制并发
总是假设最好的情况,每次拿数据都认为别人不会修改数据,所以不会加锁,但是更新的时候,会判断在此期间有没有人修改过;一般基于版本号机制实现。
-
-
表锁
- 操作里面的一条记录就锁整张表
-
行锁
- 只锁某一条记录(可能会发生死锁)
-
读锁 共享锁 (可能会发生死锁)
-
写锁 独占锁 (可能会发生死锁)
package com.xl.juc.readwrite; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; class MyCache{ private Map<String,Object> map = new HashMap<>(); private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public void put(String key,Object value){ //添加写锁 readWriteLock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+"writing...+"+key); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }finally { //释放写锁 readWriteLock.writeLock().unlock(); } map.put(key,value); System.out.println(Thread.currentThread().getName()+"write over"); } public Object get(String key){ //加上读锁 readWriteLock.readLock().lock(); Object result = null; System.out.println(Thread.currentThread().getName()+"reading...+"+key); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } finally{ //释放读锁 readWriteLock.readLock().unlock(); } result = map.get(key); System.out.println(Thread.currentThread().getName()+"read over"); return result; } } /** * @author stone */ public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache(); //创建线程写数据 for (int i = 1; i <= 5; i++) { final int num = i; new Thread(()->{ myCache.put(String.valueOf(num),String.valueOf(num)); },String.valueOf(i)).start(); } //创建线程读数据 for (int i = 1; i <= 5; i++) { final int num = i; new Thread(()->{ myCache.get(String.valueOf(num)); },String.valueOf(i)).start(); } } }
-
锁的降级 写的同时可以读
public static void main(String[] args) { ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock(); ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock(); //锁的降级 //1.获取写锁 writeLock.lock(); System.out.println("hello stone"); //2.获取读锁 readLock.lock(); System.out.println("read hello stone"); //3.释放写锁 writeLock.unlock(); //4.释放读锁 readLock.unlock(); }
hello stone read hello stone