酱爆鸭脖

导航

JUC学习!

JUC

1、what?

JUC就是java.util.concurrent下面的类包,专门用于多线程的开发。

2、why?

解决多线程、高并发

 

3、how?
   ||

   ||

  ﹀

point1:volatile关键字与内存可见性;

question1:多个线程共享资源时,彼此不可见; 可以给资源加锁,但是加锁,线程会阻塞,效率较低;如何在不使用锁的情况下,更新资源,即给资源加上volatile关键字。

 

point2:原子性

原子变量:Java提供了原子变量,在java.util.concurrent.atomic包下; 使用CAS(Compare And Swap)来保证原子性;当内存值==预估值时,我才会把更新值更新到内存。但此时会出现一个ABA问题,即一个线程修改变量A,为B再改回A,此时CAS察觉不到,被修改了,当然基本数据类型是不要紧的,但如果是引用数据呢?这个对象中有多个变量,我不知道有没有被修改。因此,增加版本号就成了一个很好的选择;

 

point3:锁分段机制

在java.util.concurrent包中提供了多种并发容器类来改进同步容器类的性能。其中最主要的就是ConcurrentHashMap。 ConcurrentHashMap采用了分段锁机制,是一个线程安全的hash表,我们知道HashMap不是线程安全的,HashTable加了锁,是线程安全的,因此他效率低。而ConcurrentHashMap默认分成了16个segment,每个segment对应一个hash表,且有自己独立的锁。所以每一个线程访问一个segment,就可以并行访问了,这大大的提高了效率;

 

point4:锁

1、传统锁synchronized关键字:

a、锁代码块、锁类; b、当这个关键字锁到一个带有static的方法时,锁的是这个类模板。

class X{
    public  Synchronized void f(){
    methods body...
    }
}

2、Lock接口:

是在juc包下的,首先要声明一个可重入锁; 有三个实现类:ReentrantLock(可重入锁 常用)、 ReentrantReadWriteLock.ReadLock(读锁)、ReentrantReadWriteLock.WriteLock(写锁)

class X{
    Lock lock = new ReentrantLock();
    public void f(){
        lock.lock;
        try{
            method body.....
        }finally{
            lock.unlock;
        }
    }
}

 

  

  1、公平锁:十分公平,先来后到;

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

3.lock与Synchronized的区别

>1、Synchronized是内置关键字;Lock是接口;
>2、Synchronized无法判断获取锁的状态;Lock可以判断;
>3、Synchronized会自动释放锁;Lock需要手动释放,不释放会出现死锁现象;
>4、Synchronized(当a线程获取锁并阻塞,b线程会一直等);Lock不会这样(tryLock()方法,当目前可以获取锁时返回true,反之false)
>5、Synchronized是可重入锁、不可中断、非公平锁;Lock可重入、可判断锁、公平性(可以自定义),扩展性更好;
>6、Synchronized可以锁少量同步代码;Lock可以锁大量的同步代码;

4、锁的类型

可重入锁:拿到外面的锁后,会自动拿到里面的锁;

自旋锁

 

point5:生产者和消费者问题

1、线程之间通信问题:生产者和消费者问题!

2、三部曲:判断是否需要等待 this.wait() 、业务、通知 this.notifyAll()

3、线程的虚假唤醒问题,将if()改为while()判断;

Condition condition = lock.newCondition();
condition.await();//等待
condition.signal()//通知

Condition实现精确通知

Condition condition1 = lock1.newCondition();
Condition condition2 = lock2.newCondition();
Condition condition3 = lock3.newCondition();
例子:当前condition1,要通知condition3,则要condition3.signal();

 

point6:8锁现象

1、多个线程使用同一个对象,多个线程就是使用一把锁,先调用的先执行!

2、多个线程使用同一个对象,多个线程就是使用一把锁,先调用的先执行,即使在某方法中设置了阻塞。

3、多个线程,有的线程有锁,有的线程没锁,两者之间不存在竞争同一把锁的情况,先后执行顺序是随机的。

4、a被 synchronized 修饰的方法,锁的对象是方法的调用者;b调用者不同,它们之间用的不是同一个锁,相互之间没有关系。

5、被 synchronized 和 static 同时修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。线程之间是顺序执行。

  锁Class和锁对象的区别:

    a、Class 锁 ,类模版,只有一个;

    b、对象锁 , 通过类模板可以new 多个对象。

6、被 synchronized 修饰 和 static 修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。

  Class锁是唯一的,所以多个对象使用的也是同一个Class锁。

  如果全部都锁了Class,那么这个类下的所有对象都具有同一把锁。

7、被 synchronized和static修饰的方法,锁的对象是类的class对象!唯一的同一把锁;

  只被synchronized修饰的方法,是普通锁(如对象锁),不是Class锁,所以进程之间执行顺序互不干扰。

 

解释见:https://blog.csdn.net/makyan/article/details/104524725

 

point7:集合不安全

异常:ConcurrentModificationException,并发修改异常;

解决方案:

  List<String> list =new ArrayList<String>();

  1、List<String> list = new Vector(); --->被synchronized修饰的方法效率很低;

  2、List<String> list = Collections.syncronizedList(new ArrayList<>());

  3、List<String> list = new CopyOnWriteArrayList<>();

  Set<String> set = new HashSet<>();

  1、Set<String> set = Collections.synchroniazedSet(new HashSet<>());

  2、Set<String> set = new CopyOnWriteArraySet<>(); HashSet本质就是用了HashMap的key不重复来实现去重的;

  Map<String,String> map = new HashMap<>();

  1、Map<String,String> map = Collections.synchronizedMap<>();

  2、Map<String,String>map = new ConcurrentHashMap<>();--->详情见point3

 

point8:Callable与Runable

先看一下两个接口的定义:

Callable

public interface Callable<V> {
  V call() throws Exception;
}

Runnable

interface Runnable {
  public abstract void run();
}


和明显能看到区别:

1.Callable能接受一个泛型,然后在call方法中返回一个这个类型的值。而Runnable的run方法没有返回值
2.Callable的call方法可以抛出异常,而Runnable的run方法不会抛出异常。

 

point9:常用辅助类

1、CountDownLatch 减法计数器

CountDownLatch count=new CountDownLatch(5);
        for (int i = 1; i <=5 ; i++) {
            new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"go out");
                    count.countDown();//count--
            },String.valueOf(i)).start();
        }
        count.await();//等待计数器归零,然后向下执行
        System.out.println("over");

2、CyclicBarrier 加法计数器(集齐七龙珠召唤神龙)

CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()-> System.out.println("召唤神龙"));
        for (int i = 1; i <=7 ; i++) {
            final int temp = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"收集第"+temp+"个龙珠");
                    try {
                        cyclicBarrier.await();//等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
            },String.valueOf(i)).start();
        }

3、Semaphore 流量

停车位,限流;三个车位,六辆车;

//有3个停车位,有六辆车
        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();
        }

 

 

point10:读写锁

ReentrantReadWriteLock 看一下jdk源码:

point11:阻塞队列BlockingQueue

1、使用情况:
2、四种API:

 

方法抛出异常有返回值,不抛异常阻塞等待超时等待
添加 add() offer() put offer(Object o,Long timeout,TimeUnit t)
移除 remove() poll() take poll(Long timeout,TimeUnit t)
检测队首 element() peek    
/**
 *  抛出异常
 */
public static void test1(){
    ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
    System.out.println(arrayBlockingQueue.add("a"));
    System.out.println(arrayBlockingQueue.add("b"));
    System.out.println(arrayBlockingQueue.add("c"));
    //检查队首元素
    System.out.println(arrayBlockingQueue.element());
    System.out.println("=================");
    System.out.println(arrayBlockingQueue.remove());
    System.out.println(arrayBlockingQueue.remove());
    System.out.println(arrayBlockingQueue.remove());
}

 

/**
 * 有返回值,不抛出异常
 */
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("c"));
    //查看队首元素
    System.out.println(blockingQueue.peek());
    System.out.println("=======================");
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());

}

 

/**
 * 阻塞等待
 */
public static void test3(){
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
    try {
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        System.out.println("===");
        blockingQueue.take();
        blockingQueue.take();
        blockingQueue.take();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

 

/**
 * 超时等待
 */
public static void test4(){
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);
    try {
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS));
        System.out.println("====================");
        System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

 

 

point12:SynchronousQueue同步队列

  同步队列和阻塞队列不同,同步队列不存储元素; 往里面put一个元素,必须要先take取出来;

 

point13:线程池(重点)

1、为什么要用线程池?线程池的好处?

>a、降低资源的消耗,不用频繁的创先销毁线程;
>b、提高响应速度
>c、方便管理

2、三大方法、七大参数、四种拒绝策略

  A、三大方法
//不要使用Executors工具类来创建线程池,用最原生的ThreadPoolExecutor;
ExecutorService threadPool= Executors.newSingleThreadExecutor();//单个线程
ExecutorService threadPool1=Executors.newFixedThreadPool(5);//固定线程
ExecutorService threadPool2= Executors.newCachedThreadPool();//可变线程,遇强则强,遇弱则弱;
try{
    for(int i=0;i<10;i++){
        //使用线程池创建线程
        threadPool.execute(()->{
            System.out.println(Thread.currentThread().getName()+"OK");
        })
    }
}finally{
    threadPool.shoutdown();//线程池用完,程序关闭,最后关闭线程池
}
  B、七大参数

  ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,//核心线程数
                           int maximumPoolSize,//允许最大线程数
                           long keepAliveTime,//当线程数大于核心线程数时,这就是多余空闲线程的存活时间
                           TimeUnit unit,//时间单位
                           BlockingQueue<Runnable> workQueue,//在执行任务之前用于保留任务的队列。 此队列将仅保存execute方法提交的Runnable任务
                           ThreadFactory threadFactory,//执行过程中创建新的线程所需要的工厂
                           RejectedExecutionHandler handler//拒绝策略,当线程满了、阻塞队列也满了时会执行的策略
                           )

 

  C、4种拒绝策略RejectedExecutionHandler
new ThreadPoolExecutor.AbortPolicy();//银行满了,还有人进来,不处理,抛异常;
new ThreadPoolExecutor.CallerRunsPolicy();//哪里来的回哪里
new ThreadPoolExecutor.DiscardOldestPolicy();//队列满了尝试和最早的竞争,不会抛出异常;
new ThreadPoolExecutor.DiscardPolicy();//队列满了丢掉任务,不会抛异常;

3、最大线程怎么定义(调优)

A、CPU密集型 获取当前CPU的核数:Runtime.getRuntime().availableProcessors() 最大线程==当前CPU核数

B、IO密集型 判断程序中十分耗IO的线程 最大线程 > 十分耗IO的线程数

 

point14:四大函数式接口(简化编程模型

函数型接口

Function function = (str)->{ return str;};//有参数、有返回值

 

断定型接口

Predicate<String> predicate =(str)->{return str.isEmpty();};//有参数,返回布尔值

 

消费性接口

Consumer<String> consumer =(str)->{ System.out.println("已消费"); };//有参数,无返回值

 

供给型接口

Supplier supplier=()->{return "你好";};//无参数,有返回值

 

 

ponit15:Stream流

/* 筛选条件
* 1、id为偶数
* 2、年龄大于22
* 3、名字转为大写
* 4、倒序
* 5、输出一个
*/
User u1 =new User(1,"a",20);
User u2 =new User(2,"b",21);
User u3 =new User(3,"c",22);
User u4 =new User(4,"d",23);
User u5 =new User(5,"e",24);
User u6 =new User(6,"f",25);
List<User> list = Arrays.asList(u1, u2, u3, u4,u5,u6);
list.stream().filter(u->{return u.getId()%2==0;})
        .filter(u->{return u.getAge()>22;})
        .map(u->{return u.getName().toUpperCase();})
        .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
        .limit(1)
        .forEach(System.out::println);

 

 

point16:ForkJoin

把一个大任务拆成多个小任务并行执行 例如:计算1-10亿内的数累加 使用ForkJoin可以提升速度,使用Stream流会更快

 

point17:异步回调

ComletableFuture

posted on 2020-12-15 11:40  酱爆鸭脖  阅读(86)  评论(0编辑  收藏  举报