JUC并发编程

JUC并发编程

并发和并行

并发:

  • cpu一核,模拟出来多条线程,快速交替

并行:

  • cpu多核,多个线程可以同时执行;线程池
//获得当前电脑的核心数
public class Demo1 {
    public static void main(String[] args) {
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}
线程的几个状态

线程状态。线程可以处于以下状态之一:

  • NEW
    尚未启动的线程处于此状态。
  • RUNNABLE
    在Java虚拟机中执行的线程处于此状态。
  • BLOCKED
    被阻塞等待监视器锁定的线程处于此状态。
  • WAITING
    正在等待另一个线程执行特定动作的线程处于此状态。
  • TIMED_WAITING
    正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
  • TERMINATED
    已退出的线程处于此状态。

一个线程可以在给定时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。

wait/sleep区别
  • wait是Object类中的方法,sleep是Thread类中的方法
  • wait会释放锁,sleep不会释放锁
  • wait只能再同步代码块中使用,sleep可以再任何地方
  • sleep必须捕获超时等待异常

Lock锁

如何判断锁的是谁

new:锁的是对象(this)

static:锁的是类(class)

Synchronized与Lock的区别
  • Synchronized内置的java关键字,Lock是一个java类
  • Synchronized无法判断锁的状态,Lock可以判断是否获得锁
  • Synchronized会自动释放锁,Lock必须手动释放锁
  • Lock锁可以通过lock.tryLock()方法尝试获取锁
  • Synchronized 可重入锁,不可以中断的,非公平;
  • Lock,可重入锁,可以判断锁,可以自己设置是否公平
  • Synchronized适合锁少量代码同步问题,Lock锁大量同步代码
传统Synchronized 同步代码块

synchronized本质:队列,锁

public class SaleTicketDemo02 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(()->{
            for (int i = 1; i <60 ; i++) {
                ticket.sale();
            }
        },"1").start();
        new Thread(()->{
            for (int i = 1; i <60 ; i++) {
                ticket.sale();
            }
        },"2").start();
        new Thread(()->{
            for (int i = 1; i <60 ; i++) {
                ticket.sale();
            }
        },"3").start();

    }
}
//资源类 OOP编程
class Ticket{
    private int number = 50;
    public synchronized void sale(){
        if (number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票,剩余"+number+"张票");
        }
    }
}
使用JUC的Lock锁

三个实现类

  • ReentrantLock 可重入锁
  • ReadLock 读锁
  • WriteLock 写锁

ReentrantLock 中有公平锁(FairSync)和非公平锁(NonfairSync)

公平锁:十分公平,可以先来后到

非公平锁:十分不公平,可以插队(默认)

public class SaleTicketDemo03 {
    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
        new Thread(()->{
            for (int i = 1; i <60 ; i++) {
                ticket.sale();
            }
        },"1").start();
        new Thread(()->{
            for (int i = 1; i <60 ; i++) {
                ticket.sale();
            }
        },"2").start();
        new Thread(()->{
            for (int i = 1; i <60 ; i++) {
                ticket.sale();
            }
        },"3").start();

    }
}

//资源类 OOP编程
//1.new 一个ReentrantLock();
//2.lock.lock(); 加锁
//3.lock.unlock();解锁
class Ticket2{
    private int number = 50;
    Lock lock = new ReentrantLock();
    public  void sale(){
        //加锁
        lock.lock();
        try{
        if (number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票,剩余"+number+"张票");
        }
        }finally {
            //解锁
            lock.unlock();
        }

    }
}

生产者和消费者问题

Synchronized解决生产者消费者问题
/*
* 线程通信问题,生产者消费者问题
* */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"a").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"b").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"c").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"d").start();
    }

}
//资源类
class Data{
    private int number = 0;
    //+1
    public synchronized void increment() throws InterruptedException {
        while (number!=0){
          //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"-->"+number);
        //通知其他线程
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        while (number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"-->"+number);
        //通知其他线程
        this.notifyAll();
    }
}
Lock解决生产者消费者问题
public class B {
    public static void main(String[] args) {
        Data2 data2 = new Data2();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                try {
                    data2.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"a").start();
        new Thread(()->{ for (int i = 0; i <10 ; i++) {
            try {
                data2.decrement();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }},"b").start();
    }
}

class Data2 {
    private int number = 0;
    final Lock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition();
    final Condition notEmp = lock.newCondition();
    public void increment() throws InterruptedException {
        //加锁
        lock.lock();
        try{
        while (number!=0){
            //等待
            notEmp.await();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+number);
        //唤醒
        notFull.signal();
        }finally {
        //解锁
        lock.unlock();
            
        }
    }
    public void decrement() throws InterruptedException {
        //加锁
        lock.lock();
        try{
        while (number==0){
            //等待
            notFull.await();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+number);
        //唤醒
        notEmp.signal();
        }finally {
        //解锁
        lock.unlock();
        }
    }
}

集合类不安全

List不安全
public class ListTest {
    public static void main(String[] args) {
        /*
        * java.util.ConcurrentModificationException
        * 并发修改异常,线程不安全
        * 解决方案:
        *   1. List<String> list = new Vector<>(); JDK1.0就有了,将add方法加入synchronized关键字,不推荐
        *   2. List<String> list = Collections.synchronizedList(new ArrayList<>());使用集合工具类转化
        *   3. List<String> list = new CopyOnWriteArrayList<>();JUC提供的,使用Lock锁,写入的时候复制一份,写完set回去
        * */
        //List<String> list = new ArrayList<>();
        //List<String> list = new Vector<>();
        // List<String> list = Collections.synchronizedList(new ArrayList<>());
         List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <=10 ; i++) {
            new Thread(()->{
             list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();

        }

    }
}
Set不安全
public class SetTest {
    public static void main(String[] args) {
        /*
        * java.util.ConcurrentModificationException
        * 同样的并发错误
        * 解决方法:
        *   1.Set<String> set = Collections.synchronizedSet(new HashSet<>());集合工具类转换
        *   2.Set<String> set = new CopyOnWriteArraySet<>();
        * */
//        Set<String> set = new HashSet<>();
//        Set<String> set = Collections.synchronizedSet(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();
        }
    }
}
HashSet的底层是什么

底层就是HashMap(源码)

public HashSet() {
    map = new HashMap<>();
}

add方法是什么(源码)

就是HashMap的put方法,利用了HashMap的Key不重复的机制,令他的Value为一个常量,用Key来保存数据

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
Map不安全
public class MapTest {
    public static void main(String[] args) {
        /*
        * java.util.ConcurrentModificationException
        * 并发修改异常
        * 解决方案:
        *   1.Map<String, String> map = Collections.synchronizedMap(new HashMap<>());集合工具类转换
        *   2.Map<String, String> map = new ConcurrentHashMap<>();
        * */
        // Map<String, String> map = new HashMap<>();
        //Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
        Map<String, String> map = 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();
        }
    }
}
ConcurrentHashMap的底层原理

https://www.cnblogs.com/chengxiao/p/6842045.html

Callable

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同,不是run()而是call()

Callable无法直接执行,他需要利用FutureTask跟Runnable产生关系,然后执行

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread myThread = new MyThread();
        FutureTask task = new FutureTask(myThread);
        new Thread(task).start();
        String o = (String) task.get();
        //get方法可能会产生阻塞,把他放到最后,或者使用异步
        System.out.println("返回的结果:"+o);
    }
}
class MyThread implements Callable<String>{
    @Override
    public String call(){
        System.out.println("call()");
        return "1234";
    }
}

常用的辅助类

1、CountDownLatch
//减法计数器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        //总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"Go Out");
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        //等待计数器归零,然后向下执行
        countDownLatch.await();
        System.out.println("克里斯关下门");
    }
}
原理:

countDownLatch.countDown();//数量减一

countDownLatch.await(); //等待计数器归零,然后向下执行

每次调用countDown计数器减一,假设计数器变为0,countDownLatch.await();就会被唤醒继续执行

2、CyclicBarrier
//加法计数器
public class CyclicbarrierTest {
    public static void main(String[] args) {
        //当线程数达到指定个数,执行参数中的指定线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙");
        });
        for (int i = 1; i <=7 ; i++) {
            final int temp = i;
            //lambda表达式读取不到i,需要利用final来辅助
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"收集"+temp+"个龙珠");
                try {
                    //等待
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
3、Semaphore
public class SemaphoreTest {
    public static void main(String[] args) {
        //参数默认线程数量
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                //acquire 得到
                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 {
                    //release 释放
                    semaphore.release();

                }

            },String.valueOf(i)).start();
        }
    }
}
原理:

semaphore.acquire();获得信号量-1,假如已经满了,等待知道被释放为止

semaphore.release();释放,会将信号量释放+1,然后唤醒等待线程

作用:多个共享资源互斥的使用!并发限流,控制最大线程数

读写锁

读锁:共享锁

写锁:独占锁

public class ReadWriteLockTest {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
        }
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }
    }
}

//自定义一个缓存
class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();
    final ReadWriteLock lock = new ReentrantReadWriteLock();

    //写入的时候只有一个线程再写
    public void put(String key, Object value) {
        lock.writeLock().lock();
        try {

            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入OK");
        } finally {
            lock.writeLock().unlock();
        }
    }

    //所有人都可以读
    public Object get(String key) {
        lock.readLock().lock();
        Object o = null;
        try {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
             o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取OK");
        } finally {
            lock.readLock().unlock();
        }
        return o;
    }
}

阻塞队列

写入:如果队列满了,就必须阻塞等待读取

读取:如果队列空的,就必须阻塞等待写入

BlockingQueue

接口的实现类

  • ArrayBlockingQueue 数组
  • DelayQueue 延时
  • LinkedBlockingQueue 链表
  • SynchronousQueue 同步队列

阻塞队列的使用:对线程并发处理,线程池!

ArrayBlockingQueue
方式抛出异常有返回值,无异常阻塞 等待超时等待
添加addofferputoffer(,)
移除removepolltakepoll(,)
检测队首元素elementpeek
      /*
    * 抛出异常
    * */
    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("----------------------------");
        System.out.println(blockingQueue.element());
        System.out.println("----------------------------");
       // System.out.println(blockingQueue.add("d"));//添加第四个,抛出异常 java.lang.IllegalStateException: Queue full
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println("----------------------------");
        System.out.println(blockingQueue.element());//没有元素了,抛出异常java.util.NoSuchElementException
       // System.out.println(blockingQueue.remove());//当读取第四个,抛出异常 java.util.NoSuchElementException
    }
/*
* 不抛出异常
* */
public static void test2(){
    //队列大小
    ArrayBlockingQueue 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.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());//null 不抛出异常
    System.out.println(blockingQueue.peek());//null 不抛出异常
}
/*
* 等待,阻塞(一致阻塞)
* */
public static void test3() throws InterruptedException {
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    //一直阻塞
    blockingQueue.put("a");
    blockingQueue.put("b");
    blockingQueue.put("c");
   //blockingQueue.put("d");//没有位置一致阻塞
    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
   //System.out.println(blockingQueue.take());//没有元素一致阻塞
}
/*
* 等待,阻塞(等待超时)
* */
public static void test4() throws InterruptedException {
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    //阻塞
    blockingQueue.offer("a");
    blockingQueue.offer("b");
    blockingQueue.offer("c");
    blockingQueue.offer("d", 2,TimeUnit.SECONDS);
    System.out.println("-----------------");
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
}
SynchronousQueue

和其他BlockingQueue不一样,SynchronousQueue 不存储元素,put了一个元素必须从里面取出来,不然不能再put

public class SynchronousQueueTest {
    public static void main(String[] args) {
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + "put1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "put2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "put3");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {

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

线程池

线程池的好处:线程复用,控制最大并发数,可以管理线程

  1. 减低资源消耗
  2. 提高响应的速度
  3. 方便管理
线程池:三大方法、七大参数、四种拒绝策略
三大方法
Executors.newSingleThreadExecutor();//单个线程
Executors.newFixedThreadPool(5);//创建固定的线程池的大小
Executors.newCachedThreadPool();//可伸缩的
//Executors 工具类的三大方法
//使用线程池来创建线程
public class Demo01 {
    public static void main(String[] args) {
        // ExecutorService threadpool = Executors.newSingleThreadExecutor();//单个线程
        // ExecutorService threadpool = Executors.newFixedThreadPool(5);//创建固定的线程池的大小
        ExecutorService threadpool = Executors.newCachedThreadPool();//可伸缩的
        try {
            for (int i = 0; i < 100; i++) {
                //使用线程池创建线程
                threadpool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } finally {
            //线程池需要关闭
            threadpool.shutdown();
        }

    }
}
七大参数

三大方法的源码

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
     return new ThreadPoolExecutor(0, Integer.MAX_VALUE//21亿,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}

三大方法都使用了ThreadPoolExecutor方法创建

这个方法的参数就是七大参数

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
七大参数
  • int corePoolSize 核心线程池大小
  • int maximumPoolSize 最大核心线程池大小
  • long keepAliveTime 超时了没有人调用就会释放(空闲线程的存活时间)
  • TimeUnit unit 超时单位
  • BlockingQueue workQu 阻塞队列
  • ThreadFactory threadFactory 线程工厂:创建线程的,一般不用动
  • RejectedExecutionHandler handler 拒绝策略
四种拒绝策略
new ThreadPoolExecutor.AbortPolicy(); //超出最大承载报异常
new ThreadPoolExecutor.CallerRunsPolicy());//超出的哪来的回哪去;例如当前程序超出的就由main线程执行
new ThreadPoolExecutor.DiscardPolicy(); //队列满了,丢掉任务,不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy();//队列满了,尝试去和最早的竞争,失败丢掉任务,成功正常执行
最大线程如何定义
  • CPU密集型:定义为CPU的核数

    Runtime.getRuntime().availableProcessors()//获取当前电脑的CPU核数
    
  • IO 密集型:判断程序中十分耗IO的线程,可以设置为IO线程的两倍

四大函数式接口

只有一个方法的接口叫做函数式接口

Function
public interface Function<T, R> {
    R apply(T t);
}

输入T类型,放回R类型

public class Demo01 {
    public static void main(String[] args) {
        Function<String,String> function = new Function<String,String>() {
            @Override
            public String apply(String o) {
                return o;
            }
        };
        System.out.println(function.apply("aaa"));
        //使用lambda表达式
        Function<String,String> function2 = (str)->{ return str; };
        System.out.println(function2.apply("bbb"));
    }
}
Predicate

断定式接口

public interface Predicate<T> {
    boolean test(T t);
}

输入T类型参数,固定返回boolean类型

public class Demo02 {
    public static void main(String[] args) {
        Predicate<String> predicate = new Predicate<String>(){
            @Override
            public boolean test(String s) {
                return s.isEmpty();
            }
        };
        System.out.println(predicate.test(""));
        //使用lambda表达式
        Predicate<String> predicate2 = (str)->{ return str.isEmpty(); };
        System.out.println(predicate2.test("123"));
    }
}
Consumer

消费型接口

public interface Consumer<T> {
    void accept(T t);
}

只有输入类型T ,没有返回值

public class Demo03 {
    public static void main(String[] args) {
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String o) {
                System.out.println(o);
            }
        };
        consumer.accept("123");
        //使用lambda表达式
        Consumer<String> consumer2 = (str)->{ System.out.println(str); };
        consumer2.accept("456");
    }
}
Supplier

供给型接口

public interface Supplier<T> {
    T get();
}

没有参数,只有T类型返回值

public class Demo04 {
    public static void main(String[] args) {
        Supplier<String> supplier = new Supplier<String>() {
            @Override
            public String get() {
                return "1024";
            }
        };
        System.out.println(supplier.get());
        //使用lambda表达式
        Supplier<String> supplier2 = ()->{ return "2048"; };
        System.out.println(supplier2.get());
    }
}

Stream流式计算

public class StreamTest {
    /*
    * 使用一行代码实现
    *   1.筛选id为偶数
    *   2.年龄大于23岁
    *   3.用户名转为大写
    *   4.用户名倒叙排列
    *   5.只输出一个用户
    * */
    public static void main(String[] args) {
        User u1 = new User(1, "a", 21);
        User u2 = new User(2, "b", 23);
        User u3 = new User(3, "c", 25);
        User u4 = new User(4, "d", 27);
        User u5 = new User(5, "e", 29);
        User u6 = new User(6, "f", 30);
        List<User> users = Arrays.asList(u1, u2, u3, u4, u5,u6);
        users.stream()
                .filter((u)->{return u.getId()%2==0;})
                .filter((u)->{return u.getAge()>23;})
                .map((u)->{return u.getName().toUpperCase();})
                .sorted((o1,o2)->{return o2.compareTo(o1);})
                .limit(1)
                .forEach(System.out::println);

    }
}

ForkJoin

ForkJoin(分支合并)JDK1.7,并行执行任务!提高效率。大数据量

ForkJoin特点:工作窃取

这里面维护的都是双端队列

public class Demo01 extends RecursiveTask<Long> {
    private Long start;
    private Long end;
    //临界值
    private Long temp = 10000l;

    public Demo01(Long start, Long end) {
        this.start = start;
        this.end = end;
    }
    public void test(){
        if ((end-start)>temp){
            //分支合并计算

        }else {
            int sum=0;
            for (int i = 0; i <=10000 ; i++) {
                sum+=i;
            }
            System.out.println(sum);
        }
    }
//计算方法
    @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 ;
            Demo01 demo01 = new Demo01(start,middle);
            demo01.fork();
            Demo01 demo02 = new Demo01(middle+1,end);
            demo02.fork();
            return demo01.join()+demo02.join();
        }
    }
}
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //test1();//sum=500000000500000000时间8909
        //test2();//sum=500000000500000000时间6840
        test3();//sum=500000000500000000时间820
    }
    public static void test1(){
        long start = System.currentTimeMillis();
        Long sum=0l;
        for (Long i =1l; i <=10_0000_0000 ; i++) {
            sum+=i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+"时间"+(end-start));
    }
    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> submit = forkJoinPool.submit(new Demo01(0l, 10_0000_0000l));
        Long aLong = submit.get();
        long end = System.currentTimeMillis();
        System.out.println("sum="+aLong+"时间"+(end-start));
    }
    public static void test3(){
        long start = System.currentTimeMillis();
        //Stream并行流
        long reduce = LongStream.rangeClosed(0, 10_0000_0000).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum="+reduce+"时间"+(end-start));
    }
}

异步回调

Future :对未来的某个事件进行建模

/*
 * 异步调用:CompletableFuture
 * 异步执行
 * 成功回调
 * 失败回调
 * */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //没有返回值的异步回调runAsync
        /*CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"runAsync=>void");
        });
        System.out.println("1111111111111111111");
        completableFuture.get();
        */
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "runAsync=>void");
            int  i =10/0;
            return 1024;
        });
        System.out.println(completableFuture.whenComplete((t, u) -> {
            //t正常结果
            System.out.println("t=>" + t);
            //u错误信息
            System.out.println("u=>" + u);
        }).exceptionally(
                (t) -> {
                    System.out.println(t.getMessage());
                    return 233;
                }).get());
    }
}

JMM

Volatile 是java虚拟机提供的轻量级的同步机制

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

什么是JMM

JMM:java内存模型,不存在的东西,概念 约定

JMM的一些同步的约定
  1. 线程解锁前,必须把共享变量,立刻刷回主存
  2. 线程加锁前,必须读取主存中的最新值到工作内存空间
  3. 加锁和解锁是同一把锁
内存交互操作有8种

虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

  • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
  • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
  • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
  • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
  • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
  • write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
JMM对这八种指令的使用,制定了如下规则:
  • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
  • 不允许一个线程将没有assign的数据从工作内存同步回主内存
  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
  • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

Volatile

保证可见性
public class JMMDemo01 {
    //如果不加volatile程序会陷入死循环
    private volatile static int num = 0;
    public static void main(String[] args) {
        new Thread(()->{
            while (num==0){

            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}
不保证原子性

原子性:线程再执行任务的时候,不能被打扰的,也不能分割

public class VDemo02 {
    private volatile static  int num = 0;
    public  static void add(){
        num++;
    }

    public static void main(String[] args) {
        //结果应该为2w 出来的结果不一定是2w
        for (int i = 1; i <=20 ; i++) {
            new Thread(() -> {
                for (int j = 1; j <= 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount()>2) {
            Thread.yield();
        }
            System.out.println(Thread.currentThread().getName()+""+num);

    }
}
不使用synchronized和Lock解决

使用JUC中的原子类

public class VDemo02 {
    //使用JUC中的原子性的Integer
    private volatile static AtomicInteger num =  new AtomicInteger(0);
    public  static void add(){
        //加一操作,使用底层的CAS
        num.getAndIncrement();
    }

    public static void main(String[] args) {
        //结果应该为2w 出来的结果不一定是2w
        for (int i = 1; i <=20 ; i++) {
            new Thread(() -> {
                for (int j = 1; j <= 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount()>2) {
            Thread.yield();
        }
            System.out.println(Thread.currentThread().getName()+""+num);

    }
}
禁止指令重排
什么是指令重排

源代码–>编译器优化重拍–>指令并行也可能重拍–>内存系统也可能重拍–>执行

单例模式

核心思想(构造器私有)
饿汉模式
public class Hungry {
    private Hungry(){

    }
    private final static  Hungry HUNGRY = new Hungry();
    public static Hungry getInstance(){
        return HUNGRY;
    }
}

无论使不使用都会加载,比较耗费资源。

懒汉模式
public class LazyMan {
    private static  boolean dong = false;
    private LazyMan() {
        synchronized (LazyMan.class){
            if (dong == false){
                dong = true;
            }else {
                throw new RuntimeException("不要试图破坏");
            }
        }
    }
//防止指令重排
    private volatile static LazyMan lazyMan;
//双重检测锁模式  懒汉式单例 DCL懒汉式
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();//不是一个原子性操作
                    /*
                    * 1. 分配内存空间
                    * 2. 执行构造方法,初始化对象
                    * 3. 把这个对象指向这个空间
                    * */
                }
            }
        }
        return lazyMan;
    }

普通的懒汉模式,可以利用反射来破坏它的单例模式。

静态内部类实现懒汉式
//静态内部类
public class Holder {
    private Holder(){

    }
    public static  Holder getInstance(){
        return InnerClass.HOLDER;
    }
    public static  class  InnerClass{
        private static  final Holder HOLDER = new Holder();
    }
}
枚举 enum 实现懒汉模式

本身就是一个Class类

//enum枚举
public enum  EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

CAS

CAS

比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么执行操作,如果不是就循环。

//CAS compareAndSet :比较并交换
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        //如果期望的值(2020)达到了就更新(2021)
        //CAS 是CPU的并发原语
        atomicInteger.compareAndSet(2020,2021);
        System.out.println(atomicInteger.get());
        atomicInteger.compareAndSet(2020,2022);
        System.out.println(atomicInteger.get());
    }

}

缺点:

  1. 循环会耗时
  2. 一次性只能保证一个共享变量的原子性
  3. ABA问题
ABA
//ABA问题
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        //====================捣乱的线程=========================
        //将2020该为2021,然后又将2021改为2020
        atomicInteger.compareAndSet(2020,2021);
        System.out.println(atomicInteger.get()); 
        atomicInteger.compareAndSet(2021,2020);
        System.out.println(atomicInteger.get());
        //===================期望的线程==========================
        //这里将2020改为6666,他就不知道之前这个数据被修改过 
        atomicInteger.compareAndSet(2020,6666);
        System.out.println(atomicInteger.get());
    }

}

原子引用

阿里巴巴开发手册
【强制】所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。
说明:对于 Integer var = ?-128127 范围内的赋值,Integer 对象是在
IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行
判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,
推荐使用 equals 方法进行判断。
使用原子引用解决ABA问题(乐观锁原理)
//ABA问题
public class CASDemo {
    public static void main(String[] args) {
        //AtomicInteger atomicInteger = new AtomicInteger(2020);

        //这里因为Inter的原因 数值只能在-128~127之间
        AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(12,1);
        new Thread(()->{
            int stamp = atomicInteger.getStamp();//获得版本号  1
            System.out.println("a1=>"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //乐观锁原理相同
            System.out.println(atomicInteger.compareAndSet(12, 13, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
            System.out.println("a2=>"+atomicInteger.getStamp());
            System.out.println(atomicInteger.compareAndSet(13, 12, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
            System.out.println("a3=>"+atomicInteger.getStamp());
        },"a").start();
        new Thread(()->{
            int stamp = atomicInteger.getStamp();//获得版本号  1
            System.out.println("b1=>"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicInteger.compareAndSet(12, 120, stamp, stamp + 1));
            System.out.println("b2=>"+atomicInteger.getStamp());
        },"b").start();
    }

}

各种锁

1、公平锁、非公平锁

公平锁:非常公平,不能插队,必须先来后到

非公平锁:非常不公平,可以插队

//默认非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}
//当参数为true时,变为公平锁
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
}
2、可重入锁

可重入锁(递归锁)

//Synchronized
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{phone.sms();},"a").start();
        new Thread(()->{phone.sms();},"b").start();
    }
}
class Phone{
    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName()+"sms");
        //这里也有锁
        call();
    }
    public synchronized void call(){
        System.out.println(Thread.currentThread().getName()+"call");
    }
}
//Lock
public class Demo02 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        new Thread(() -> {
            phone.sms();
        }, "a").start();
        new Thread(() -> {
            phone.sms();
        }, "b").start();
    }
}

class Phone2 {
    Lock lock = new ReentrantLock();

    public void sms() {
        lock.lock();
        try {

            System.out.println(Thread.currentThread().getName() + "sms");
            //这里也有锁
            call();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
3、自旋锁
//自旋锁
public class SpinlockDemo {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    //加锁
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"-->myLock");
        //当atomicReference中的线程为空时,才会停止循环
        while (!atomicReference.compareAndSet(null,thread)){

        }
    }
    //解锁
    public void myUnLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"-->myUnLock");
        //释放锁将atomicReference中的线程设置为null;方便下一个解锁
        atomicReference.compareAndSet(thread,null);
    }
}

测试(必须a解锁后b才能解锁)

public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {
        SpinlockDemo spinlockDemo = new SpinlockDemo();
        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                spinlockDemo.myUnLock();
            }
        },"a").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(1);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                spinlockDemo.myUnLock();
            }
        },"b").start();

    }
}
4、死锁
public class DeadLockDemo {
    public static void main(String[] args) {
        String LockA = "LockA";
        String LockB = "LockB";
        new Thread(new MyThread(LockA,LockB)).start();
        new Thread(new MyThread(LockB,LockA)).start();
    }
}
class MyThread implements Runnable{
     private String LockA;
     private String LockB;
    public MyThread(String lockA, String lockB) {
        LockA = lockA;
        LockB = lockB;
    }

    @Override
    public void run() {
        synchronized (LockA){
            System.out.println(Thread.currentThread().getName()+"有"+LockA+"还想要"+LockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (LockB){
                System.out.println(Thread.currentThread().getName()+"有"+LockB+"还想要"+LockA);
            }
        }
    }
}
怎样排除死锁
  1. 使用 jps-l 定位进程号

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G8lC08vB-1594274266075)(C:\Users\42084\AppData\Roaming\Typora\typora-user-images\image-20200709134238528.png)]

  2. 使用 jstack 进程号找到死锁问题

S.sleep(3);
}catch (Exception e){
e.printStackTrace();
}finally {
spinlockDemo.myUnLock();
}
},“a”).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
spinlockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(1);
}catch (Exception e){
e.printStackTrace();
}finally {
spinlockDemo.myUnLock();
}
},“b”).start();

}

}


#### 4、死锁

```java
public class DeadLockDemo {
    public static void main(String[] args) {
        String LockA = "LockA";
        String LockB = "LockB";
        new Thread(new MyThread(LockA,LockB)).start();
        new Thread(new MyThread(LockB,LockA)).start();
    }
}
class MyThread implements Runnable{
     private String LockA;
     private String LockB;
    public MyThread(String lockA, String lockB) {
        LockA = lockA;
        LockB = lockB;
    }

    @Override
    public void run() {
        synchronized (LockA){
            System.out.println(Thread.currentThread().getName()+"有"+LockA+"还想要"+LockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (LockB){
                System.out.println(Thread.currentThread().getName()+"有"+LockB+"还想要"+LockA);
            }
        }
    }
}
怎样排除死锁
  1. 使用 jps-l 定位进程号
    在这里插入图片描述
  2. 使用 jstack 进程号找到死锁问题

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值