JUC详解(各种乱七八糟的锁)

文章目录

  • 1JUC
  • 2回顾多线程知识
  • 3Lock锁(重点)
  • 4Lock锁
  • 5JUC版的生产者与消费者问题(虚假唤醒)
  • 6有序线程
  • 7八锁问题
  • 8CopyOnWriteArrayList(读写复制)
  • 9Set并发问题解决方案
  • 10HashMap不安全解决方案
  • 11线程计数器
    • 11.1线程加法计数器(CyclicBarrier)
    • 11.2线程减法计数器(CountDownLatch)
  • 13读共享锁(写独占锁)(MyCache)
  • 14(BlockingQueue四组API)
  • 15同步队列(SynchronousQueue、不存储元素)
  • 16线程池(ThreadPoolExecutor)
    • 查看电脑CPU核数的代码
  • 17四大函数式接口
    • Function(输入值和返回值相同)
    • Predicate(返回布尔值、判断字符串是否为空)
    • Consumer(只有输入值没有返回值)
    • Supplier()
  • 18链式编程(Stream流式计算)
  • 19ForkJoin
    • 三种处理线程的方式
  • 20异步回调(Future)
  • 21理解JMM(Volatile)
  • 22Volatile可见性及非原子性验证
    • 不保证原子性
  • 23指令重排详解
  • 24单例模式(饿汉模式和懒汉模式)
  • 25深入理解CAS
  • 26原子引用解决ABA问题(需要引入原子引用-27节提到)
    • 26.1ABA问题
    • 26.2原子引用解决ABA问题
  • 27可重入锁
  • 28自旋锁
    • 测试自旋锁
  • 29死锁排查(jps)
    • 解决问题
  • 30乐观锁、悲观锁(无内容)

1JUC

在这里插入图片描述

2回顾多线程知识

进程和线程:
进程:一个进程可以包含多个线程,至少是一个线程,java默认2个线程——main、GC(垃圾回收)
线程:开了一个软件,写字的时候也会有自动保存的线程在进行,ThreadRunnableCallable
java开启不了线程,java操作的是底层的C++,java无法操作,用的是虚拟机
并发与并行:
并发:多线程操作同一个资源(假设cpu只有一核的时候,模拟多线程,快速交替处理线程)
并行:CPU多核情况下才有,多个线程同时执行

并发编程的本质:充分利用电脑的CPU
线程有几种状态:6种:new(创建)、runnable(运行)、blocked(阻塞)、wait(等待)、timed_waiting(超时等待)
terminated(终止)

wait与sleep的区别:
1.来自不同的类——wait(Object)、sleep(Thread2.关于锁——wait会释放锁,而sleep不会释放
3.适用范围不一样——wait必须在同步代码块中执行,sleep则不用
4.w捕获异常——wait不需要捕获异常、sleep需要捕获异常

3Lock锁(重点)

传统Synchronized解决有顺序卖票问题

package JUC;
/*基本的卖票的例子
线程是一个单独的资源类
* */
public class Demo02SaleTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }

        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }

        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}

class Ticket{
    private int num=30;
    public synchronized void sale(){
        if (num>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"票,剩余"+num);
        }
    }
}

4Lock锁

package JUC;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo03Lock {
    public static void main(String[] args) {
        Ticket1 ticket1 = new Ticket1();

    }
}

//Lock锁————三步骤——1.new ReentrantLock 2.加锁 3.解锁
class Ticket1{
    private int num=30;
    //ReentrantLock---java默认非公平锁,也就是默认可以插队(比较合理,3秒的不需要等待3小时的线程)
    Lock lock=new ReentrantLock();
    public void sale(){
        lock.lock();//加锁
        try {
            //业务代码
            if (num>0){
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"票,剩余"+num);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();//解锁
        }


    }

}

5JUC版的生产者与消费者问题(虚假唤醒)

package JUC;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*JUC版的生产者与消费者问题
通过Lock可以找到Condition
以下的是无序的随机的状态结果,JUC的优势还没有体现出来
ReentrantLock是可重入的意思
* */
public class Demo05JUC1 {
    public static void main(String[] args) {
        Date1 date1 = new Date1();

        new Thread(()->{for (int i = 0; i < 10; i++) {
            try {
                date1.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        },"A").start();
        new Thread(()->{for (int i = 0; i < 10; i++) {
            try {
                date1.decrement();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        },"B").start();
        new Thread(()->{for (int i = 0; i < 10; i++) {
            try {
                date1.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        },"C").start();
        new Thread(()->{for (int i = 0; i < 10; i++) {
            try {
                date1.decrement();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        },"D").start();
    }


}
class Date1{
    private int num=0;
    Lock lock=new ReentrantLock();
    Condition condition = lock.newCondition();


    public  void increment() throws InterruptedException {
        lock.lock();
        try {
            while (num!=0){
                //等待
                //this.wait();JUC之前普通的
                condition.await();
            }
                num++;
                System.out.println(Thread.currentThread().getName()+"=="+num);
                //通知线程B,我+1完毕了this.notifyAll();JUC之前普通的
                condition.signalAll();//唤醒全部

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }


    public  void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (num==0){
                //等待
                //this.wait();
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"=="+num);
            //通知线程B,我-1完毕了 JUC之前的this.notifyAll();
            condition.signalAll();//唤醒全部
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

6有序线程

package JUC;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*实现生产者与消费者的有序JUC执行
三条线程按照循环顺序执行
* */
public class Demo06JUC2 {
    public static void main(String[] args) {
        new Thread(()->{},"A").start();
        new Thread(()->{},"B").start();
        new Thread(()->{},"C").start();

    }
}

//资源类
class Date3{
    private Lock lock=new ReentrantLock();
    private Condition condition = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int num=1;

    public void print1(){
        lock.lock();
        try {//业务代码,判断-执行-通知
        if (num!=1){
            condition.await();
        }
        System.out.println(Thread.currentThread().getName()+"1111");
        //通知唤醒
            num=2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void print2(){
        lock.lock();
        try {
            if (num!=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"2222");
            num=3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void print3(){
        lock.lock();
        try {
            if (num!=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"3333");

            num=1;
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

7八锁问题

package JUC;

import java.util.concurrent.TimeUnit;

/*什么是锁,锁的对象是谁——查看自己的收藏笔记
8锁,就是关于锁的8个问题
1.标准情况下,线程先执行哪个?————先发短信再打电话(原因:锁的存在)
2.发短信延迟4秒的情况下————还是先短信再电话,先执行发短信,因为有同步锁
3.增加了一个普通方法后,是执行发短信还是hello,限制性普通方法,普通方法不受锁的限制
4.两个对象的情况下,先执行发短信还是打电话——————打电话,因为两把锁不一样,打电话的锁有延迟
5.静态方法的时候,两个线程先执行哪个?——————加了static以后锁的就是class对象,类的模板只有一个所以和原先锁的是对象没有关系

* */
public class Demo07suo1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone1 = new Phone();
        //第一条线程
        new Thread(()->{
            phone.send();
        },"A").start();
        //A线程睡眠
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //第二条线程
        new Thread(()->{
            phone1.call();
        },"B").start();

    }
}
//资源类
class Phone{
    public  static synchronized void send(){        //synchronized锁的是方法的调用者,两个功能都是phone的调用,锁的对象是phone
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public  synchronized   void call(){
        System.out.println("打电话");
    }
}

8CopyOnWriteArrayList(读写复制)

(ConcurrentModificationException并发修改异常,不能一起修改)

package JUC;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/*.ConcurrentModificationException并发修改异常,不能一起修改
并发下ArrayList不安全————结局方案:
1.List list1 =new Vector<>();
2. List list1 = Collections.synchronizedList(new ArrayList<>());
3. List list1 = new CopyOnWriteArrayList<>();它比Vector效率高原因在于Vector用到了synchronized,导致了效率低
4.
* */
public class Demo11Unsafe {
    public static void main(String[] args) {
        List<String> list1 = new CopyOnWriteArrayList<>();
     /*   CopyOnWriteArrayList写入时复制COW计算机程序设计领域的一种优化策略
     在写入的时候复制一份,出现覆盖的问题,造成数据问题
      */
        for (int i = 0; i < 10; i++) {
            list1.add(UUID.randomUUID().toString().substring(0,5));//生成随机字符串
            new Thread(()->{
                System.out.println(list1);
            },String.valueOf(i)).start();//添加十条线程
        }

    }
}

9Set并发问题解决方案

package JUC;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

/*Set还是会出现并发问题
HashSet的底层就是hashmap
add set 本质就是map ,map key是无法重复的
解决方案:
1.Set set = Collections.synchronizedSet(new HashSet<>());
2.Set set =new CopyOnWriteArraySet<>();
3.
* */
public class Demo12SetTest {
    public static void main(String[] args) {
       // Set set = Collections.synchronizedSet(new HashSet<>());
        new HashSet<>();
        Set<String> set =new CopyOnWriteArraySet<>();
        for (int i = 1; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();

        }
    }
}

10HashMap不安全解决方案

package JUC;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/*HashMap本质也不是安全的
解决方法:
1.Map map1 = new ConcurrentHashMap<>();
2.ConcurrentHashMap原理
* */
public class Demo13HashMapTest {
    public static void main(String[] args) {
        Map<String , String > map = new HashMap<>();
        Map<String , String > map1 = new ConcurrentHashMap<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

11线程计数器

11.1线程加法计数器(CyclicBarrier)

package JUC;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/*加法计数器
线程达到一定数量的时候开始执行操作,集齐7颗龙珠就可以召唤神龙
* */
public class Demo15CyclicBarrier {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("完成加法计数");
        });
        for (int i = 1; i <= 7; i++) {
            final int a=i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"收集"+a);
                try {
                    cyclicBarrier.await();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }

    }
}

11.2线程减法计数器(CountDownLatch)

package JUC;

import java.util.concurrent.CountDownLatch;

/*JUC常用三大类:一、
*计数器(减法计数器)
*
* */
public class Demo14CountDownLatch {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);//总数是6,必须要执行任务的时候再使用
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"go");
                countDownLatch.countDown();//数量-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await();//等待计数器归0,然后再向下执行
        System.out.println("close");
    }
}

#12线程等待释放离开、等待一定时间停车位释放(Semaphore)

package JUC;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/*信号量
线程数量:停车位,有限的线程分给几个对象相互占用,限流————控制线程数
semaphore.acquire()————获得,假设线程满了就等待释放
semaphore.release()————释放
* */
public class Demo16Semaphore {
    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(2);
                    System.out.println(Thread.currentThread().getName()+"离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();//释放
                }
            },String.valueOf(i)).start();
        }
    }
}

13读共享锁(写独占锁)(MyCache)

package JUC;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/*读写锁
读锁(共享锁)多个线程可以同时占有
写锁(独占锁)一次只能被一个线程锁占用
* */
public class Demo17ReentrantReadWriteLock {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        //写入的操作

        for (int i = 1; i <= 5; i++) {
            final int a=i;//lambda表达式无法访问外部的变量
            new Thread(()->{
                myCache.put(a+"",a+"");
            },String.valueOf(i)).start();
        }
        for (int i = 1; i <= 5; i++) {
            final int a=i;//lambda表达式无法访问外部的变量
            new Thread(()->{
                myCache.get(a+"");
            },String.valueOf(i)).start();
        }
    }

}
class MyCache{
    private volatile Map<String ,Object> map=new HashMap<>();
    private ReadWriteLock readWriteLock=  new ReentrantReadWriteLock();  //读写锁创建
    //存,写
    public void put(String key,Object value){
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入完毕");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }

    }
    //取,读
    public void get(String key){
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取完毕");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }

    }
}

14(BlockingQueue四组API)

添加、删除、队列首元素、超时等待等等方法

package JUC;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

/*BlockingQueue四组API,queue是队列的意思
添加、删除、队列首元素、超市等待等等方法
* */
public class Demo18BlockingQueue {
    public static void main(String[] args) {
     /*   test1();
        test2();*/
        try {
            test3();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void test1(){//满了会抛出异常
        //队列的大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        System.out.println(blockingQueue.element());
        // System.out.println(blockingQueue.add("d"));
       //取出元素
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //System.out.println(blockingQueue.remove());.NoSuchElementException异常
       // System.out.println(blockingQueue.remove());

    }
    public static void test2(){//满了不会抛出异常
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("d"));//false,不会抛出异常
        System.out.println(blockingQueue.peek());

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());//不会阻塞,直接显示空的值


    }
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        blockingQueue.put("d");//现在的这种情况就是一直的等待,因为已经满了,所以要等待移除出去以后才可以开始添加
        blockingQueue.take();
        blockingQueue.take();
        blockingQueue.take();
        blockingQueue.take();//没有元素的时候也是一直阻塞等待的
    }
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        blockingQueue.offer("d",3, TimeUnit.SECONDS);//超时等待,超过3秒就退出
        blockingQueue.poll(2,TimeUnit.SECONDS);
        blockingQueue.poll(2,TimeUnit.SECONDS);
        blockingQueue.poll(2,TimeUnit.SECONDS);
        blockingQueue.poll(2,TimeUnit.SECONDS);//取出,超过两秒就退出
    }

}

15同步队列(SynchronousQueue、不存储元素)

package JUC;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/*同步队列
和其他的BlockingQueue不一样,SynchronousQueue不存储元素
put了一个值,必须从里面先take出来,否则不能再put进去
* */
public class Demo19SynchronousQueue {
    public static void main(String[] args) {
        BlockingQueue<String > blockingQueue = new SynchronousQueue<>();//同步队列
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()+"put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName()+"put 3");
                blockingQueue.put("3");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
            }
        },"A").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=="+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=="+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=="+blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();

    }
}

16线程池(ThreadPoolExecutor)

/*线程池(重点)
线程池的好处:
1.降低资源的消耗
2.提高相应的速度
3.方便管理
线程三大方法、7大参数、4种拒绝策略(重点!!!)
3大方法:
以下Executors三大方法
7大参数:
ThreadPoolExecutor线程池执行者包含的7个参数
4种拒绝策略:
1.new ThreadPoolExecutor.AbortPolicy() 人满了,抛出异常
2.new ThreadPoolExecutor.CallerRunsPolicy(),main的线程执行,哪来的去哪里
3.new ThreadPoolExecutor.DiscardPolicy()线程满了,丢掉任务,不会抛出异常
4.new ThreadPoolExecutor.DiscardOldestPolicy());线程满了先和最早的线程去竞争,然后争不过的额时候、丢掉任务,不会抛出异常
使用了线程池以后就要用线程池来创建线程

线程池的大小怎么去设置:(两种)
CPU密集型:电脑是几核的就用几来定义最大线程数
IO密集型:当你的电脑有大型的任务的时候,设置线程大于IO密集型任务的个数(IO)十分占用资源

  • */
package JUC;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo20pool {
    public static void main(String[] args) {
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();//单线程
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);//设定固定的线程数
        //ExecutorService threadPool = Executors.newCachedThreadPool();//可变化的线程数量,遇强则强,遇弱则弱

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy());//两个窗口、最多5个窗口、等待3秒、候客区3个、默认线程、拒绝策略(四种)

        try {
            for (int i = 0; i < 9; i++) {
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完需要关闭
            threadPoolExecutor.shutdown();
        }


        
    }
}

查看电脑CPU核数的代码

package ceshi;

public class CPU {
    public static void main(String[] args) {
        System.out.println(Runtime.getRuntime().availableProcessors());

    }
}

17四大函数式接口

函数式四大接口:Consumer、Function、Predicate、Supplier
程序员必备技能:lambda表达式、链式编程、函数式接口、Stream流式计算

Function(输入值和返回值相同)

package JUC;

import java.util.function.Function;

/*四大函数式接口:Function
* */
public class Demo21Function {
    public static void main(String[] args) {
        //工具类:输入输出的值相等
        Function function = new Function<String,String>() {
            @Override
            public String apply(String str) {return str;}};
        System.out.println(function.apply("asd"));

        Function<String , String > function1 = (str)->{return str;};//最简化lambda表达式
    }
}

Predicate(返回布尔值、判断字符串是否为空)

package JUC;

import java.util.function.Predicate;
/*四大函数式接口:Predicate
断定型接口:有一个输入参数、返回值只能是布尔值
* */
public class Demo22Predicate {
    public static void main(String[] args) {
        //判断字符串是否为空
     /*   Predicate predicate = new Predicate() {
            @Override
            public boolean test(String  o) {
                return o.isEmpty();
            }
        };

      */
        Predicate<String> predicate = (str)->{return str.isEmpty();};//lambda表达式
        System.out.println(predicate.test("asd"));
    }
}

Consumer(只有输入值没有返回值)

package JUC;

import java.util.function.Consumer;

/*消费型接口:只有输入值,没有返回值
* */
public class Demo23Consumer {
    public static void main(String[] args) {
         /* Consumer consumer = new Consumer() {
            @Override
            public void accept(String  o) {
                System.out.println(o);
            }
        };*/
        Consumer<String > consumer =(o)->{System.out.println(o);};
        consumer.accept("byug");
    }
}

Supplier()

package JUC;

import java.util.function.Supplier;

/*供给型接口:没有参数、只有返回值
* */
public class Demo24Supplier {
    public static void main(String[] args) {
       /* Supplier objectSupplier = new Supplier() {
            @Override
            public Integer get() {
                System.out.println("get()");
                return 1024;
            }
        };*/
        Supplier<Integer> supplier =()->{System.out.println("get()");return 1024;};
        System.out.println(supplier.get());
    }
}

18链式编程(Stream流式计算)

package JUC;

import java.util.Arrays;
import java.util.List;

/*链式编程
* */
public class Demo25 {
    public static void main(String[] args) {
        User user = new User(1,"a",21);
        User user2 = new User(2,"b",23);
        User user3 = new User(3,"c",45);
        User user4 = new User(4,"d",48);
        //集合就是存储
        List<User> list = Arrays.asList(user, user2, user3, user4);
        //计算交给Stream
        //lambda表达式、链式编程、函数式接口、Stream流式计算
        //以下就是链式编程
        list.stream()
                .filter(u->{return u.getId()%2==0;})
                .filter(u->{return u.getAge()>23;})
                .map(u->{return u.getName().toUpperCase();})
                .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
                .limit(1)
                .forEach(System.out::println);



    }
}
class User{
    int id;
    String name;
    int age;
    public User() {
    }

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

19ForkJoin

特点:并行执行任务、提高效率、大数量的时候使用(注意有弊端,必须大数据的时候去使用)
ForkJoin特点:工作窃取,如图所示,当A没有完成任务的时候,B线程已经完成任务,B线程就会去窃取A线程的任务,从而达到有效利用的目的。
ForkJoin缺点:因为是双端队列,所以可能AB会同时去抢任务。所以必须大量数据的时候去使用
双端队列:可以从上往下执行,也可以从下往上执行
JUC详解(各种乱七八糟的锁)_第1张图片

三种处理线程的方式

package JUC;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;

/*线程拆分
*
* */
public class Demo26ForkJoin {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//            test1();//2000
//            test2();//2000
        test3();//200
    }
    //测试一
    public static void test1(){
        long sum=0L;
        long start=System.currentTimeMillis();
        for (long i = 1L; i <= 10_0000_0000; i++) {
            sum+=i;
        }
        long end=System.currentTimeMillis();
        System.out.println("和为"+sum+",花掉的时间为:"+(end-start));
    }
    //测试二
    public static void test2() throws ExecutionException, InterruptedException {
        long start=System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinDemo task = new ForkJoinDemo(0L,10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();

        long end=System.currentTimeMillis();
        System.out.println("和为"+sum+",花掉的时间为:"+(end-start));
    }
    //测试三
    public static void test3(){
        long start=System.currentTimeMillis();

        //Stream并行流           rangeClosed(],parallel是并行的意思,
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);

        long end=System.currentTimeMillis();
        System.out.println("和为"+sum+",花掉的时间为:"+(end-start));
    }

}
/*
1.forkjoin通过它来执行
2.计算任务forkjoinpool.execute(ForkjoinTest task)
* */
class ForkJoinDemo extends RecursiveTask<Long> {
    private Long start;
    private Long end;
    //临界值
    private Long temp=100L;

    public ForkJoinDemo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }
    public void test(){

    }
    //计算方法
    @Override
    protected Long compute() {
        if ((end-start)>temp){
            //分值计算
            Long sum=0L;
            for (Long i = start; i <= end; i++) {
                sum += i;
            }
                return sum;
        }else{
                long middle = (start + end) / 2;//中间值
                ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
                task1.fork();//拆分任务,把任务压入线程队列
                ForkJoinDemo task2= new ForkJoinDemo(middle+1, end);
                task2.fork();//拆分任务,把任务压入线程队列
                return task1.join()+task2.join();
            }
    }
}

20异步回调(Future)

Future:对将来的某个时间的结果进行建模

package JUC;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/*异步调用Future
Ajax:不需要等待一段时间得到前面的线程结果,而后可以直接去进行别的任务
异步执行
成功回调
失败回调
* */
public class Demo27 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    /*    //发起一个请求    runAsync(异步)
        //以下是没有返回值的runAsync异步回调
        CompletableFuture completableFuture = CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
        });
        System.out.println("1111");
        completableFuture.get();//获取阻塞执行结果*/




        //以下是有返回值的supplyAsync异步回调
        //ajax,成功和失败的回调,如果失败返回的是错误的信息
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
            int i=10/0;
            return 1024;
        });
        System.out.println(completableFuture.whenComplete((t, u) -> {
            System.out.println("t" + t);//正常执行返回结果
            System.out.println("u" + u);//有错误的时候返回的错误信息
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 100;//可以获取到错误的返回信息
        }).get());

    }
}

21理解JMM(Volatile)

Volatile:java虚拟机提供轻量级的同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排
JMM:java内存模型,不存在的东西,概念、约定
作用:保证线程的安全
关于JMM的一些同步约定:
1.线程加锁前,必须把共享变量刷回主存(内存里面有一份,线程拷贝得出结果以后刷回主存)
2.线程解锁前,必须读取主存中的最新值到工作内存中
3.加锁和解锁必须是同一把锁
工作内存、主内存八种操作,必须成对存在
JUC详解(各种乱七八糟的锁)_第2张图片

package JUC;

import java.util.concurrent.TimeUnit;

public class Demo28JMM {
    private static int num=0;
    public static void main(String[] args) {//main主线程
        new Thread(()->{//线程1
            while (num==0){

            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num=1;
        System.out.println(num);
    }
}

运行结果是1,但是程序没有停止
需要让A线程知道主内存中的值已经发生变化,这个时候就要用到Volatile

22Volatile可见性及非原子性验证

Volatile不保证原子性
原子性:不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割,要么同时成功,要么同时失败

package JUC;
/*不保证原子性
* */
public class Demo29Volatile {
    private  static int num=0;//volatile加上以后不能保证20000次执行结果,所以volatile不保证原子性!!
    public static  void add(){//synchronized加上以后可以保证20000次执行,
        num++;//num++本身就不是一个原子性操作
    }

    public static void main(String[] args) {
        //理论上的结果为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int i1 = 1; i1 <= 1000; i1++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount()>2){//activeCount是还存活的线程的数量,除开main和gc以外才可以0
            Thread.yield();//礼让
        }
        System.out.println(Thread.currentThread().getName()+" "+num);


    }
}

不保证原子性

package JUC;

import java.util.concurrent.atomic.AtomicInteger;

/*不保证原子性
* */
public class Demo29Volatile {
    //原子类的Integer
    private  static AtomicInteger num=new AtomicInteger();//volatile加上以后不能保证20000次执行结果,所以volatile不保证原子性!!
    public static  void add(){//synchronized加上以后可以保证20000次执行,
        //num++;//num++本身就不是一个原子性操作
       /* getAndIncrement +1方法    这个方法本质是使用了CAS,跟底层操作系统弄挂钩,所以效率比较高,在内存中修改值
       * 它是一个很特殊的存在*/
        num.getAndIncrement();
    }

    public static void main(String[] args) {
        //理论上的结果为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int i1 = 1; i1 <= 1000; i1++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount()>2){//activeCount是还存活的线程的数量,除开main和gc以外才可以0
            Thread.yield();//礼让
        }
        System.out.println(Thread.currentThread().getName()+" "+num);


    }
}

23指令重排详解

/指令重排
什么是指令重排?————你写的程序,计算机并不是按照那样子去执行
源代码>编译器优化重排>指令并行可能也会重排>内存系统可能也会重排>执行
处理器在进行指令重排的时候,会考虑数据之间的依赖性
因为指令重排会导致结果错误,所以Volatile可以避免指令重排导致的错误结果
只要加了Volatile就会产生

内存屏障CPU指令,作用***:1.可以特定的操作的顺序2.可以保证某些变量的内存可见性
(利用这些特性Volatile实现了可见性)
内存屏障的作用:中间产生内存屏障,禁止上下的指令顺序交换
Volatile可以保持可见性、不能保证原子性,但是由于内存屏障,可以保证避免出现指令重排的现象产生
Volatile在单例模式在玩的最多
单例模式:

  • */

24单例模式(饿汉模式和懒汉模式)

package JUC;
/*懒汉式单例
* */
public class Demo32LazyMan {
    private Demo32LazyMan() {
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    private volatile static Demo32LazyMan demo32LazyMan;//避免出现指令重拍错误结果,必须加Volatile
    public static Demo32LazyMan getInstance(){
        //加锁的操作,双重检测锁模式的懒汉式单例——DCL懒汉式
        if (demo32LazyMan == null) {
            synchronized (Demo32LazyMan.class){
                if (demo32LazyMan==null){
                    demo32LazyMan=new Demo32LazyMan();      //不是一个原子性操作
                    /*1.分配内存空间
                    2. 执行构造方法,初始化对象
                    3.把这个对象指向这个空间
                    * */
                }
            }
        }

        return demo32LazyMan;
    }
    //单线程下单例ok,多线程下有问题,所以需要加锁
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                demo32LazyMan.getInstance();
            }).start();
        }
    }

}

25深入理解CAS

/*什么是CAS?
比较当前工作内存中的值,如果这个值是期望的,那么执行操作,如果不是那么就一直循环
CAS:compareAndSet:比较并交换
CAS:ABA问题:(狸猫换太子)
缺点:
1.循环会耗时
2.一次性只能保证一个共享变量的原子性
3.会产生ABA问题
Unasfe类可以让java操作内存,java正常是不可以操作内存的,C++可以,所以它相当于java的后门

  • */

26原子引用解决ABA问题(需要引入原子引用-27节提到)

两条线程同时操作的时候,第二条线程第一次把3修改成1,但是对于第一条线程来说,原来的她已经不再是原来的她,虽然看起来是一样(狸猫换太子)
JUC详解(各种乱七八糟的锁)_第3张图片

26.1ABA问题

package ceshi;
import java.util.concurrent.atomic.AtomicReference;
public class Demo01 {
    //ABA问题
    public static void main(String [] args) throws InterruptedException {
        AtomicReference<String> ref=new AtomicReference<String>("A");
        String prev=ref.get();
        new Thread(()-> {
            System.out.println("change A->B "+ref.compareAndSet("A", "B"));
            //盲区,do sth
            System.out.println("change B->A "+ref.compareAndSet("B", "A"));
        }).start();
        Thread.sleep(1000);
        System.out.println("change A->C "+ref.compareAndSet(prev, "C"));
    }
}

26.2原子引用解决ABA问题

package ceshi;
import java.util.concurrent.atomic.AtomicStampedReference;
public class Demo02 {
//解决ABA问题
    /*步骤:
    1.new AtomicStampedReference            (新建原子戳引用)
    2.ref.getReference();                   (获得参考)
    3.ref.getStamp()                        (获得印章)
    4.new Thread                            (创建线程,输出原子引用实例AB之间的转换)
    5.ref.compareAndSet(prev, "C",mstamp,1)  (输出盖章为目标转换到C的结果)
    * */
        public static void main(String [] args) throws InterruptedException {
            AtomicStampedReference<String> ref=new AtomicStampedReference<String>("A",0);
            String prev=ref.getReference();
            int mstamp=ref.getStamp();
            new Thread(()-> {
                int stamp=ref.getStamp();
                System.out.println("change A->B "+ref.compareAndSet("A", "B",stamp,1));
                //不再是盲区    do sth unknown.....
                stamp=ref.getStamp();
                System.out.println("change B->A "+ref.compareAndSet("B", "A",stamp,1));
            }).start();
            Thread.sleep(1000);
            System.out.println("change A->C "+ref.compareAndSet(prev, "C",mstamp,1));
        }
    }



27可重入锁

各种锁的理解:
1.公平锁与非公平锁
公平锁:公平,不可以插队
非公平锁:不公平,可以插队,默认都是非公平锁(因为比较公平)
2.可重入锁(递归锁):

28自旋锁

package JUC;

import java.util.concurrent.atomic.AtomicReference;

/*自旋锁
* */
public class Demo38SpinLock {
    AtomicReference<Thread> atomicReference=new AtomicReference<>();

    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"myLock");
        while (!atomicReference.compareAndSet(null,thread)){

        }

    }
    public void myUnLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"myUnLock");
        atomicReference.compareAndSet(thread,null);
    }
}

测试自旋锁

package JUC;

import java.util.concurrent.TimeUnit;

public class Demo39TestSpinLock {
    public static void main(String[] args) throws InterruptedException {
        Demo38SpinLock lock = new Demo38SpinLock();
        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        },"T1").start();

        TimeUnit.SECONDS.sleep(3);

        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        },"T2").start();

    }
}

29死锁排查(jps)

package JUC;

import java.util.concurrent.TimeUnit;

/*死锁的情况
两个对象互相都想拿对方的锁
解决办法:
使用jps定位进程号
* */
public class Demo40Lock {
    public static void main(String[] args) {
        String lockA="lockA";
        String lockB="lockB";
        new Thread(new MyThread(lockA,lockB),"T1").start();
        new Thread(new MyThread(lockB,lockA),"T2").start();
    }

}
class MyThread implements Runnable{
    private String lockA;
    private String lockB;

    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get"+lockB);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>get"+lockA);

            }

        }

    }
}

解决问题

1.使用jps -l定位进程号
运行之后Terminal输入jps -l就会查处具体的线程信息
JUC详解(各种乱七八糟的锁)_第4张图片
2.使用jstack查看进程信息
输入jstack 14212
得到以下的结果
JUC详解(各种乱七八糟的锁)_第5张图片

30乐观锁、悲观锁(无内容)

这个问题可以自行查询~

你可能感兴趣的:(基础学习,java,开发语言,后端)