[java基础复习]juc-尚硅谷周阳

买票问题复习

  • 高内聚 低耦合前提下, 线程操作资源类
  • 判断/干活/通知
  • 多线程交互中, 必须要防止多线程的虚假唤醒(spurious wakeup), 也即(判断必须要用while, 不能用if).
  • 注意标志位的修改和定位.

IDEA中要生成自己的代码块的快捷键!!!

在这里插入图片描述

给IDEA增加自定义的代码模板: Idea代码模板和自定义代码模板的使用

精确通知顺序访问

package test;

/*
* 多线程之间按顺序调用,实现A->B->C
* 三个线程启动,要求如下:
*
* AA打印5次, BB打印10次, CC打印15次
* 接着
* AA打印5次, BB打印10次, CC打印15次
* 。。。。。来10轮
* */

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

//首先写一个资源类
class ShareResources {
    private int whoToPrint = 1; // 1: A 2: B, 3: C
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void print5() {
        lock.lock();
        try {
            //1. 判断
            while (whoToPrint != 1) {
                condition1.await();
            }
            //2. 干活
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + "AA");
            }
            //3. 通知
            whoToPrint = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print10() {
        lock.lock();
        try {
            //1. 判断
            while (whoToPrint != 2) {
                condition2.await();
            }
            //2. 干活
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + "BB");
            }
            //3. 通知
            whoToPrint = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void print15() {
        lock.lock();
        try {
            //1. 判断
            while (whoToPrint != 3) {
                condition3.await();
            }
            //2. 干活
            for (int i = 0; i < 15; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + "CC");

            }
            //3. 通知
            whoToPrint = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadOrderAccess {
    public static void main(String[] args) {
        ShareResources shareResources = new ShareResources();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareResources.print5();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareResources.print10();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareResources.print15();
            }
        }, "C").start();
    }
}

八锁

You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class’s static fields is controlled by a lock that’s distinct from the lock for any instance of the class.

集合不安全

LIst不安全
UUID 和 System.currentTimeMillis()的使用, 生成不重复的字符串

问题:
java.util.ConcurrentMethodModificationException
解决方法:

  • Vector
  • Collections.synchronizedList()方法
  • java.util.concurrent.CopyOnWriteArrayList
    是一种读写分离的思想,读和写不同的容器
//java.util.concurrent.CopyOnWriteArrayList中的add方法源码
public boolean add(E e) {
        synchronized(this.lock) {
            Object[] es = this.getArray();
            int len = es.length;
            es = Arrays.copyOf(es, len + 1);
            es[len] = e;
            this.setArray(es);
            return true;
        }
    }

Set不安全
和List报一样的错

解决方法:

  • Collections.synchronizedSet()方法
  • java.util.concurrent.CopyOnWriteArraySet

Map不安全
解决方法:

  • Collections.synchronizedMap()方法
  • java.util.concurrent.ConcurrentHashMap

多线程中第三种获得多线程的方式----Callable

package test;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "***********come in here");
        return 1024;
    }
}

public class CallableDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        FutureTask futureTask = new FutureTask(new MyThread());

        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start();

        System.out.println(Thread.currentThread().getName() + "******计算完成");

        System.out.println(futureTask.get());
    }
}

运行结果

注意:

扫描二维码关注公众号,回复: 11510480 查看本文章
  • FutureTaskget方法要放在最后
  • 如果两个线程初始化用的是同一个FutureTask, 那么call()方法只会执行一次.

java.util.concurrent.CountDownLatch class

package test;
import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t离开教室");
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }

        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "\t班长关门走人");
    }
}

输出结果
在这里插入图片描述

java.util.concurrent.CyclicBarrier class

package test;

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

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("**********召唤神龙");
        });

        for (int i = 0; i < 7; i++) {
            int finalI = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t收集到第" + finalI + "颗龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            }, String.valueOf(i)).start();
        }
    }
}

输出结果:
在这里插入图片描述

java.util.concurrent.Semaphore class

在这里插入图片描述

package test;

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

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "\t抢占到了车位");
                    try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
                    System.out.println(Thread.currentThread().getName() + "\t离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}

输出结果:
在这里插入图片描述

java.util.concurrent.locks.ReadWriteLock interface

BEFOR

package test;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/*
* 多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行
* 但是
* 如果有一个线程想去写共享资源类,就不应该有其他线程可以对该资源列进行读或者写
* 小总结:
*           读-读 能共存
*           读-写 不能共存
*           写-写 不能共存
* */
class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();

    void put(String key, Object value) {
        System.out.println(Thread.currentThread().getName() + "\t 开始写入数据" + key);
        try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + "\t 写入完成");
    }

    void get(String key) {
        System.out.println(Thread.currentThread().getName() + "\t ----开始读取数据");
        try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
        Object result = map.get(key);
        System.out.println(Thread.currentThread().getName() + "\t ----读取完成" + result);
    }
}

public class ReadWrieteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.get(finalI +"");
            }, String.valueOf(i)).start();
        }

        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.put(finalI +"", finalI +"");
            }, String.valueOf(i)).start();
        }
    }
}

输出结果:
在这里插入图片描述
AFTER

package test;

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

/*
* 多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行
* 但是
* 如果有一个线程想去写共享资源类,就不应该有其他线程可以对该资源列进行读或者写
* 小总结:
*           读-读 能共存
*           读-写 不能共存
*           写-写 不能共存
* */
class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    void put(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 开始写入数据" + key);
            try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 写入完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    void get(String key) {
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t ----开始读取数据");
            try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t ----读取完成" + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

public class ReadWrieteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.get(finalI +"");
            }, String.valueOf(i)).start();
        }

        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                myCache.put(finalI +"", finalI +"");
            }, String.valueOf(i)).start();
        }
    }
}

输出结果:
在这里插入图片描述

阻塞队列java.util.concurrent.BlockingQueue

阻塞队列
在这里插入图片描述
阻塞队列的用处
在这里插入图片描述
阻塞队列的种类分类
在这里插入图片描述

在这里插入图片描述
BlockingQueue核心方法

在这里插入图片描述

线程池

为什么要用线程池?
在这里插入图片描述
Executor框架的组织架构
在这里插入图片描述
基本用法概览

package test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();//一池N个工作线程,类似与一个银行有N个受理窗口
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);//一池5个工作线程,类似与一个银行有5个受理窗口
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();//一池1个工作线程,类似与一个银行有1个受理窗口

        try {
            for (int i = 0; i < 10; i++) {
                //try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
                threadPool.execute(() -> {
                    try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
                    System.out.println(Thread.currentThread().getName() + "\t办理业务");
                });
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

运行结果
在这里插入图片描述
线程池的7个参数
请看ThreadPoolExecutor类的构造方法的源码:

public ThreadPoolExecutor(int corePoolSize, 
                                  int maximumPoolSize, 
                                  long keepAliveTime, 
                                  TimeUnit unit, 
                                  BlockingQueue<Runnable> workQueue, 
                                  ThreadFactory threadFactory, 
                                  RejectedExecutionHandler handler) {
            this.ctl = new AtomicInteger(ctlOf(-536870912, 0));
            this.mainLock = new ReentrantLock();
            this.workers = new HashSet();
            this.termination = this.mainLock.newCondition();
            if (corePoolSize >= 0 && maximumPoolSize > 0 && maximumPoolSize >= corePoolSize && keepAliveTime >= 0L) {
                if (workQueue != null && threadFactory != null && handler != null) {
                    this.corePoolSize = corePoolSize;
                    this.maximumPoolSize = maximumPoolSize;
                    this.workQueue = workQueue;
                    this.keepAliveTime = unit.toNanos(keepAliveTime);
                    this.threadFactory = threadFactory;
                    this.handler = handler;
                } else {
                    throw new NullPointerException();
                }
            } else {
                throw new IllegalArgumentException();
            }
        }

七个参数的解释:
在这里插入图片描述
线程池的工作原理
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

线程池的拒绝策略
JDK有四种内置策略,这四种内置策略均实现了RejectedExecutionHandle接口

这四种内置策略是:
在这里插入图片描述

package test;

import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
        try {
            for (int i = 0; i < 10; i++) {
                int finalI = i;
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t正在办理业务" + finalI);
                });
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

运行结果:
在这里插入图片描述

  • 在cpu密集型的任务中,自定义线程池时,最大线程数是cpu的核数+1,可以用Runtime.getRuntime().availableProcessors()获取cpu的核数.
  • 在io密集型任务中,由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*CPU数目。

链式编程, 流式计算
在这里插入图片描述
在这里插入图片描述
java.util.function内置的四大函数式接口
在这里插入图片描述

ForJoinDemo

在这里插入图片描述
在这里插入图片描述

package test;

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

class MyTask extends RecursiveTask<Integer> {
    private static final Integer ADJUST_VALUE = 10;

    private int begin;
    private int end;
    private int result;

    MyTask(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

    @Override
    protected Integer compute() {

        if ((end-begin) <= ADJUST_VALUE) {
            for (int i = begin; i <= end; i++) {
                result = result + i;
            }
        }else{
            int middle = (begin + end) / 2;
            MyTask task01 = new MyTask(begin, middle);
            MyTask task02 = new MyTask(middle + 1, end);
            task01.fork();
            task02.fork();
            result = task01.join() + task02.join();
        }
        return result;
    }
}

public class ForkJoinDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyTask myTask = new MyTask(0, 100);

        ForkJoinPool forkJoinPool = new ForkJoinPool();

        ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask);

        System.out.println(forkJoinTask.get());

        forkJoinPool.shutdown();
    }
}

输出结果: 5050

异步调用java.util.concurrent.CompletableFuture

package test;

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

public class CompletableFutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "没有返回值,update mysql ok");
        });
        //try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println(completableFuture.get());
        System.out.println("****************************************");

        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 有返回值的");
            int age = 10/0;
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            return 1024;
        });


        System.out.println(
                completableFuture1.whenCompleteAsync((t, u) -> {
                    System.out.println("******t: " + t);
                    System.out.println("******u: " + u);
                }).exceptionally((f) -> {
                    System.out.println(f.getMessage());
                    return 4444;
                }).get()
        );
    }
}

输出结果
在这里插入图片描述

Serializable接口, ·Cloneable·接口
什么是lombok注解?

猜你喜欢

转载自blog.csdn.net/qq_27637285/article/details/107638058