Java并发编程--基础进阶高级完整笔记。
这都不知道是第几次刷狂神的JUC并发编程了,从第一次的迷茫到现在比较清晰,算是个大进步了,之前JUC笔记不见了,重新做一套笔记。
参考链接:https://www.bilibili.com/video/BV1B7411L7tE
[TOC]
🔥1.多线程--基础内容
1.Thread状态
6种:新建、运行、阻塞、等待、超时等待、结束(可点击Thread查看源代码)
public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }
2.Synchronized
- 非公平锁
- 可重入锁,
Synchronized:是非公平锁(不能保证线程获得锁的顺序,即线程不会依次排队去获取资源,而是争抢,但是结果一定是正确的),是可重入锁(已获得一个锁,可以再获得锁且不会造成死锁,比如synchronized内部可以再写个synchronized函数)
/** * Author: HuYuQiao * Description: Synchronized实现方式(修饰函数即可) */ class TicketSync{ public int number = 50; //synchronized本质是队列,锁 public synchronized void sale(){ if(number > 0) { System.out.println(Thread.currentThread().getName() + "获得了第" + number-- +"票"); } } }
3.Lock锁
可重入锁
公平还是不公平锁可以设置(默认不公平锁)
/** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
Lock:加锁之后必须解锁,,否则其他线程就获取不到了,所以用
try-catch-finally
包起来。
/** * Author: HuYuQiao * Description: Lock实现方式(加锁、解锁) */ class TicketLock{ Lock lock = new ReentrantLock(); public int number = 50; public void sale(){ lock.lock(); try { System.out.println(Thread.currentThread().getName() + "获得了第" + number-- +"票"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
4.总结
在不加锁情况下,多线程会争抢,导致输出顺序、计算结果都会不一致(上面例子结果如果一样是因为只有3个线程,for循环即出错,因为number--这个函数本身不是线程安全的),所以就引入了锁的概念,synchronized,lock保证了输出顺序、计算结果的一致性。
虚假唤醒:在synchronized.wait与lock.condition.await唤醒线程时,是从await代码之后开始运行,所以为了保证能唤醒线程,需要用while语句将代码包含起来。
完整代码
package com.empirefree.springboot; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import sun.security.krb5.internal.Ticket; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @program: springboot * @description: 多线程 * @author: huyuqiao * @create: 2021/06/26 14:26 */ @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class ThreadTest { /** * Author: HuYuQiao * Description: Synchronized实现方式(修饰函数即可) */ class TicketSync{ public int number = 50; //synchronized本质是队列,锁 public synchronized void sale(){ System.out.println(Thread.currentThread().getName() + "获得了第" + number-- +"票"); } } /** * Author: HuYuQiao * Description: Lock实现方式(加锁、解锁) */ class TicketLock{ Lock lock = new ReentrantLock(); public int number = 50; public void sale(){ lock.lock(); try { System.out.println(Thread.currentThread().getName() + "获得了第" + number-- +"票"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } @Test public void testThread() { // TicketSync ticket = new TicketSync(); TicketLock ticket = new TicketLock(); new Thread( () ->{ for (int i = 0; i < 50; i++) { ticket.sale(); } },"ThreadA").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { ticket.sale(); } },"ThreadB").start(); new Thread(()->{ for (int i = 0; i < 50; i++) { ticket.sale(); } },"ThreadC").start(); for (int i = 0; i < 500; i++) { new Thread(() -> { ticket.sale(); }).start(); } } }
🔥2.八锁现象(synchronized、static)
即synchronized、static修饰的函数,执行顺序、输出结果。
结果表明:
1.synchronized修饰的函数:会锁住对象(可以看成锁对象中某个方法),看起来代码会依次执行,而没有锁的方法即不受影响,一来就先执行
2.static synchronized修饰的函数:会锁住类.class(可以不同对象访问的都是同一个函数),所以2个对象访问自己的函数依然还是顺序执行.
3.一个有static,一个没有static:即一个锁类.class,另一个锁对象,不管是同一个对象还是不同对象,就都不需要等待了,不会顺序执行。
1.synchronized
修饰函数会保证同一对象依次顺序执行()
class Phone{ //synchronized public synchronized void sendSms() { try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendSms"); } public synchronized void call() { System.out.println("call"); } public void playGame(){ System.out.println("playGame"); } } public static void main(String[] args) { //Thread--代码执行顺序问题 Phone phone = new Phone(); new Thread(phone::sendSms, "A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(phone::call, "B").start(); new Thread(phone::playGame, "C").start(); }
2.static synchronized
class PhoneStatic{ //static synchronized public static synchronized void sendSmsStatic() { try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sendSmsStatic"); } public static synchronized void callStatic() { System.out.println("callStatic"); } } public static void main(String[] args) { PhoneStatic phoneStatic = new PhoneStatic(); PhoneStatic phoneStatic2 = new PhoneStatic(); new Thread(() ->{ phoneStatic2.sendSmsStatic(); }, "A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() ->{ phoneStatic2.callStatic(); }, "B").start(); }
🔥3.Java集合--安全性
//集合安全性--list,set,map都非线程安全 List<String> list = new Vector<>(); List<String> list = Collections.synchronizedList(new ArrayList<>()); List<String> list = new CopyOnWriteArrayList<>(); Map<String, String> objectObjectHashMap = new ConcurrentHashMap<>(); Map<Object, Object> objectObjectHashMap1 = Collections.synchronizedMap(new HashMap<>()); //set底层就是map:无论hashset还是linkedhashset Set<String> set = Collections.synchronizedSet(new LinkedHashSet<>()); Set<String> set = new CopyOnWriteArraySet<>();
🔥4.高并发--辅助类
学习链接:https://www.cnblogs.com/meditation5201314/p/14395972.html
1.countdownLatch
Countdownlatch:减一操作,直到为0再继续向下执行
package Kuangshen.JUC.Thread; import java.util.concurrent.CountDownLatch; public class countDownLatch { public static void main(String[] args) throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(5); for (int i = 0; i < 5; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "get out"); countDownLatch.countDown(); }, String.valueOf(i)).start(); } countDownLatch.await(); //等待上述执行完毕再向下执行 System.out.println("close door"); } }
2.cyclicbarrier
Cyclicbarrier:+1操作,对于每个线程都自动+1并等待,累计到规定值再向下执行,
package Kuangshen.JUC.Thread; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * @author :Empirefree * @description:TODO * @date :2021/2/10 10:56 */ public class cyclicbarrier { public static void main(String[] args) throws BrokenBarrierException, InterruptedException { //CyclicBarrier里面是容量 + runnable final CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () ->{ System.out.println("不断增加到7即向后执行,与countdownlatch相反"); } ); /*对于指派的局部变量,lambda只能捕获一次 ,故而需定义成final(int内部定义就是final),而且线程中, 不能对局部变量进行修改,如需要修改,需定义成原子类atomic */ for (int i = 0; i < 7; i++) { int finalI = i; new Thread(() ->{ System.out.println(finalI); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } } }
3.semaphore
semaphore:对于规定的值,多个线程只规定有指定的值能获取,每次获取都需要最终释放,保证一定能互斥执行
package Kuangshen.JUC.Thread; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * @author :Empirefree * @description:TODO * @date :2021/2/10 15:24 */ public class semaphore { public static void main(String[] args) { final Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 60; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "抢到车位"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName() + "离开车位"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }).start(); } } }
🔥5.读写锁(ReadWriteLock)
ReadWriteLock也是多线程下的一种加锁方式,下面列出ReadWriteLock和synchronized对多线程下,保证读完之后在写的实现方式
//读写锁 :写只有一个线程写,写完毕后 可以多个线程读 MyCache myCache = new MyCache(); int num = 6; for (int i = 1; i < num; i++) { int finalI = i; new Thread(()->{ myCache.write(String.valueOf(finalI),String.valueOf(finalI)); },String.valueOf(i)).start(); } for (int i = 1; i < num; i++) { int finalI = i; new Thread(()->{ myCache.read(String.valueOf(finalI)); },String.valueOf(i)).start(); } class MyCache{ private volatile Map<String,String> map = new HashMap<>(); private ReadWriteLock lock = new ReentrantReadWriteLock(); //存,写 public void write(String key,String value){ lock.writeLock().lock(); //写锁 try { System.out.println(Thread.currentThread().getName()+"线程开始写入"); map.put(key,value); System.out.println(Thread.currentThread().getName()+"线程开始写入ok"); } catch (Exception e){ e.printStackTrace(); } finally { lock.writeLock().unlock(); } } //取,读 public void read(String key){ lock.readLock().lock(); //读锁 try { System.out.println(Thread.currentThread().getName()+"线程开始读取"); map.get(key); System.out.println(Thread.currentThread().getName()+"线程读取ok"); } catch (Exception e){ e.printStackTrace(); } finally { lock.readLock().unlock(); } } //存,写 public synchronized void writeSync(String key,String value){ try { System.out.println(Thread.currentThread().getName()+"线程开始写入"); map.put(key,value); System.out.println(Thread.currentThread().getName()+"线程开始写入ok"); } catch (Exception e){ e.printStackTrace(); } } //取,读 public void readSync(String key){ try { System.out.println(Thread.currentThread().getName()+"线程开始读取"); map.get(key); System.out.println(Thread.currentThread().getName()+"线程读取ok"); } catch (Exception e){ e.printStackTrace(); } } }
🔥6.线程池
1.集合--队列(阻塞队列、同步队列)
阻塞队列:blockingQueue(超时等待--抛弃,所以最后size=1)
//阻塞队列 ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1); arrayBlockingQueue.offer("a", 2, TimeUnit.SECONDS); arrayBlockingQueue.offer("a", 2, TimeUnit.SECONDS); System.out.println("超时等待==" + arrayBlockingQueue.size());
同步队列:synchronizeQueue(类似于生产者消费者)
//同步队列 SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>(); new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+"put 01"); synchronousQueue.put("1"); System.out.println(Thread.currentThread().getName()+"put 02"); synchronousQueue.put("2"); System.out.println(Thread.currentThread().getName()+"put 03"); synchronousQueue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+"take"+synchronousQueue.take()); System.out.println(Thread.currentThread().getName()+"take"+synchronousQueue.take()); System.out.println(Thread.currentThread().getName()+"take"+synchronousQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }).start();
2.线程池基本概念(三大方法、七大参数、四种拒绝策略)
三大方法:newSingleThreadExecutro(单个线程),newFixedThreadPool(固定大小线程池),newCachedThreadPool(可伸缩)
ExecutorService threadPool = Executors.newSingleThreadExecutor(); ExecutorService threadPool2 = Executors.newFixedThreadPool(5); ExecutorService threadPool3 = Executors.newCachedThreadPool();
七大参数:
ThreadPoolExecutor(int corePoolSize, //核心线程池大小 int maximumPoolSize, //最大的线程池大小(当阻塞队列满了就会打开) long keepAliveTime, //(空闲线程最大存活时间:即最大线程池中有空闲线程超过这个时间就会释放线程,避免资源浪费) TimeUnit unit, //超时单位 BlockingQueue<Runnable> workQueue, //阻塞队列 ThreadFactory threadFactory, //线程工厂 创建线程的 一般不用动 RejectedExecutionHandler handler //拒绝策略
四种拒绝策略:
new ThreadPoolExecutor.AbortPolicy: // 该 拒绝策略为:银行满了,还有人进来,不处理这个人的,并抛出异常 new ThreadPoolExecutor.CallerRunsPolicy(): // //该拒绝策略为:哪来的去哪里 main线程进行处理 new ThreadPoolExecutor.DiscardPolicy(): //该拒绝策略为:队列满了,丢掉异常,不会抛出异常。 new ThreadPoolExecutor.DiscardOldestPolicy(): //该拒绝策略为:队列满了,尝试去和最早的进程竞争,不会抛出异常
🔥7.Stream(4个函数式接口、Lambda、异步回调)
1.函数式接口
学习链接:https://www.cnblogs.com/meditation5201314/p/13693089.html
2.Lambda表达式
学习链接:https://www.cnblogs.com/meditation5201314/p/13651755.html
3.异步回调
//异步回调--无返回值 CompletableFuture<Void> future = CompletableFuture.runAsync(() ->{ try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "...."); }); System.out.println("begin"); System.out.println(future.get()); System.out.println("end"); //异步回调--有返回值 CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() ->{ try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return 3; }); System.out.println("begin"); System.out.println(future2.get()); System.out.println(future2.whenComplete((result, error) ->{ System.out.println("返回结果:" + result); System.out.println("错误结果" + error); }).exceptionally(throwable -> { System.out.println(throwable.getMessage()); return 502; }).get()); System.out.println("end");
🔥8.单例模式
1.饿汉模式(程序一启动就new,十分占内存)
/** * @program: untitled * @description: 单例模式 * @author: huyuqiao * @create: 2021/06/27 14:44 */ public class SingleModel { /* * 可能会浪费空间 * */ private byte[] data1 = new byte[1024*1024]; private byte[] data2 = new byte[1024*1024]; private byte[] data3 = new byte[1024*1024]; private byte[] data4 = new byte[1024*1024]; private static final SingleModel hugrySingle = new SingleModel(); private SingleModel(){ } public static SingleModel getInstance(){ return hugrySingle; } }
2.懒汉模式(DCL模式:双重检测,需要的时候才new)
1.第一层if没有加锁,所以会有多个线程到达if
2.如果内部new对象指令重排,就会导致有些线程认为lazyManModel有对象,所以会直接返回lazyManModel(实际为null)
3.所以需要加上valitile防止指令重排
/** * @program: untitled * @description: 懒汉式 * @author: huyuqiao * @create: 2021/06/27 15:06 */ public class LazyManModel { private volatile static LazyManModel lazyManModel; private LazyManModel(){ System.out.println(Thread.currentThread().getName() + "..."); } //DCL懒汉式:双重检测锁--实现效果,只有为空的才null,否则不用null,所以需要2重if判断。 public static LazyManModel getInstance(){ if (lazyManModel == null){ synchronized (LazyManModel.class){ if (lazyManModel == null){ lazyManModel = new LazyManModel(); } } } return lazyManModel; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() ->{ LazyManModel.getInstance(); }).start(); } } }
🔥9.Volatile和Atomic
学习笔记:https://www.cnblogs.com/meditation5201314/p/13707590.html
🔥10.Java中锁
1.公平锁(FIFO):Lock锁可以自定义,synchronized
2.非公平锁(允许插队):Lock锁可以自定义
3.可重入锁(获取一个锁再获取其他锁不会造成死锁):lock锁和synchronized
4.自旋锁:得不到就一直等待(Atomic.getAndIncrement底层就是自旋锁)
import java.util.Collections; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; /** * @program: untitled * @description: spinlock * @author: huyuqiao * @create: 2021/06/27 15:40 */ public class SpinLockTest { public static void main(String[] args) throws InterruptedException { //使用CAS实现自旋锁 SpinlockDemo spinlockDemo=new SpinlockDemo(); new Thread(()->{ spinlockDemo.myLock(); try { TimeUnit.SECONDS.sleep(3); } catch (Exception e) { e.printStackTrace(); } finally { spinlockDemo.myUnlock(); } },"t1").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ spinlockDemo.myLock(); try { TimeUnit.SECONDS.sleep(3); } catch (Exception e) { e.printStackTrace(); } finally { spinlockDemo.myUnlock(); } },"t2").start(); } } class SpinlockDemo { // 默认 // int 0 //thread null AtomicReference<Thread> atomicReference=new AtomicReference<>(); //加锁 public void myLock(){ Thread thread = Thread.currentThread(); System.out.println(thread.getName()+"===> mylock"); //自旋锁--为空则返回true,否则返回false while (!atomicReference.compareAndSet(null,thread)){ System.out.println(Thread.currentThread().getName()+" ==> .自旋中~"); } } //解锁 public void myUnlock(){ Thread thread=Thread.currentThread(); System.out.println(thread.getName()+"===> myUnlock"); atomicReference.compareAndSet(thread,null); } }
5.死锁命令排查
jps -l jstack 进程号