Java并发编程--基础进阶高级(完结)

499 阅读9分钟
Java并发编程--基础进阶高级完整笔记。

这都不知道是第几次刷狂神的JUC并发编程了,从第一次的迷茫到现在比较清晰,算是个大进步了,之前JUC笔记不见了,重新做一套笔记。

参考链接:www.bilibili.com/video/BV1B7…


1.多线程--基础内容

  • 1.Thread状态
  • 2.Synchronized
  • 3.Lock锁
  • 4.总结

☕️ 2.八锁现象(synchronized、static)

  • 1.synchronized
  • 2.static synchronized

3.Java集合--安全性

4高并发--辅助类

  • 1.countdownLatch
  • 2.cyclicbarrier
  • 3.semaphore

5读写锁(ReadWriteLock)

6线程池

  • 1.集合--队列(阻塞队列、同步队列)
  • 2.线程池基本概念(三大方法、七大参数、四种拒绝策略)

7.Stream(4个函数式接口、Lambda、异步回调)

  • 1.函数式接口

  • 2.Lambda表达式

  • 3.异步回调 8.单例模式

  • 1.饿汉模式(程序一启动就new,十分占内存)

  • 2.懒汉模式(DCL模式:双重检测,需要的时候才new)

9.Volatile和Atomic

10.Java中锁

  • 1.公平锁(FIFO):Lock锁可以自定义,synchronized
  • 2.非公平锁(允许插队):Lock锁可以自定义
  • 3.可重入锁(获取一个锁再获取其他锁不会造成死锁):lock锁和synchronized
  • 4.自旋锁:得不到就一直等待(Atomic.getAndIncrement底层就是自旋锁)
  • 5.死锁命令排查



🔥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.高并发--辅助类

​ 学习链接:www.cnblogs.com/meditation5…

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.函数式接口

​ 学习链接:www.cnblogs.com/meditation5…


2.Lambda表达式

​ 学习链接:www.cnblogs.com/meditation5…


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

​ 学习笔记:www.cnblogs.com/meditation5…

🔥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 -ljstack 进程号