0.6-JUC

JUC

1.什么是JUC

java.util.concurrent

2.进程和线程

java默认两个线程:main,gc

进程:一段程序的执行过程

线程:进程中一个单一顺序的控制流

java代码不能开启一个线程,通过native方法调用C++开启


并发:交替执行

并行:同时执行

//获取cpu核数
        System.out.println(Runtime.getRuntime().availableProcessors());

并发编程的本质:充分利用CPU资源


java中定义的线程状态

  • NEW :新生
  • RUNNABLE :运行
  • BLOCKED :阻塞
  • WAITING :等待
  • TIMED_WAITING :超时等待
  • TERMINATED :终止

wait / sleep区别

  1. 来自不同类
    wait --> Object
    sleep --> Thread
  2. 关于锁的释放
    wait释放锁,sleep不释放
  3. 使用范围不同
    wait在同步代码块中
    sleep任何地方

3.Lock

synchronized 和 Lock区别

  1. synchronized是java内置关键字,Lock是java类

  2. synchronized无法判断获取锁的状态,Lock可判断是否获取了锁

  3. synchronized会自动释放锁,Lock必须手动释放

  4. synchronized 若线程1获得该锁并阻塞,线程2一直等待获取该锁;Lock不一定会一直等待

  5. synchronized可重入锁,不可以中断,非公平;Lock可重入锁,非公平(可设置)

    可重入锁:某个线程已经获取该锁,若再次获取该锁,可以获取,不会出现死锁

    synchronized(this){

    ​ //第一次获取

    ​ synchronized(this){

    ​ //第二次获取,可获取,

    ​ }

    }

  6. synchronized适合锁少量的代码同步问题,Lock适合大量

4.生产者和消费者问题

synchronized (obj) {
   while (<condition does not hold>)
       obj.wait();
   ... // Perform action appropriate to condition
} 
synchronized
Lock
wait()
condition.await()
notifyAll()
condition.signalAll()
synchronized(obj){
    while(<cond>)
        obj.wait();
    //...
    obj.notifyAll();
}

/**Lock 等效代码*/
//成员变量
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();

//代码块
lock.lock();
try{
    while(<cond>)
        condition.await();
    //...
    condition.signalAll();
} finally{
    lock.unlock();
}

Condition新技术

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

//Data的printA printB printC依次执行
public class Test {
    public static void main(String[] args) {
        Data d = new Data();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                d.printA();
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                d.printB();
            }
        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                d.printC();
            }
        },"C").start();
    }
}

class Data{
    private final Lock lock = new ReentrantLock();
    //每个condition监视一个资源
    private final Condition condition1 = lock.newCondition();
    private final Condition condition2 = lock.newCondition();
    private final Condition condition3 = lock.newCondition();

    private int flag = 1; // 1.printA 2.printB 3.printC

    public void printA(){
        lock.lock();
        try{
            while(flag!=1)
                condition1.await();
            System.out.println(Thread.currentThread().getName()+"==>A");
            //指定唤醒
            flag = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try{
            while(flag!=2)
                condition2.await();
            System.out.println(Thread.currentThread().getName()+"==>B");
            //指定唤醒
            flag = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try{
            while(flag!=3)
                condition3.await();
            System.out.println(Thread.currentThread().getName()+"==>C");
            //指定唤醒
            flag = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

5.八锁现象

锁是什么,如何判断锁的是谁

关于synchronized实现同步

​ synchronized 修饰方法:

​ 普通成员方法,锁的是该类的对象

​ static方法,锁的是该类的Class对象

​ 未被synchronized修饰的方法不需要获取锁对象

6.集合类不安全

List

//并发下ArrayList不安全
/**解决方案
*1. List list = new Vector();
*2. List list = Collections.synchronizedList(new ArrayList());
*3. List list = new CopyOnWriteArrayList();
*/

//CopyOnWrite 写时复制

Set

/**
*1. Set set = Collection.synchronizedSet(new HashSet());
*2. Set set = new CopyOnWriteArraySet();
*/

HashSet:

java HashSet 部分源码:

public HashSet() {
    map = new HashMap<>();
}
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

PRESENT是一个常量

map的key值本身要求是不重复的

Map

HashMap:

加载因子(DEFAULT_LOAD_FACTOR = 0.75)

初始化容量 (16)

/**
* HashMap map = new ConcurrentHashMap();
*/

7.Callable

  • 可以有返回值
  • 可以抛出异常
class A implements Callable<String>{ //<String> 泛型对应返回值类型
    @Override
    public String call() throws EXception{
        //...
    }
}

//使用Callable启动线程
FutureTask ft = new FutureTask(new A());
new Thread(ft).start();
String re = (String)ft.get(); //获取返回结果,可能会阻塞,等待结果
/**解释
*1. FutureTask 实现 RunnableFuture接口,RunnableFuture 继承 Runnable和Future接口
*2. FutureTask可传入Callable对象
*3. Thread传入Runnable对象,开启线程
/

以下代码输出:

call

YES

注意:只有一个 call 被打印

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

public class TestCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> f = new FutureTask<>(new A());
        new Thread(f).start();
        new Thread(f).start(); //结果会被缓存,提高效率
        System.out.println(f.get());
    }
}

class A implements Callable<String>{
    @Override
    public String call() throws Exception {
        System.out.println("call");
        return "YES";
    }
}

可取消的异步计算。 该类提供了一个Future基本实现 ,具有启动和取消计算的方法,查询计算是否完整,并检索计算结果。 结果只能在计算完成后才能检索; 如果计算尚未完成,则get方法将阻止。 一旦计算完成,则无法重新启动或取消计算(除非使用runAndReset()调用计算 )。

8.常用的辅助类

8.1 CountDownLatch

import java.util.concurrent.CountDownLatch;

public class TestCountDownLatch {
    public static void main(String[] args) throws InterruptedException {

        CountDownLatch c = new CountDownLatch(6); //减法计数器

        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" executed");
                c.countDown(); //减1
            }).start();
        }

        c.await(); //等待计数器归零,然后向下执行
        System.out.println("All executed");

    }
}

8.2 CyclicBarrier

循环栅栏

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

public class TestCyclicBarrier {
    public static void main(String[] args) {
        CyclicBarrier c = new CyclicBarrier(7,()->{
            System.out.println("YES");
        }); //加法计数器,当c.await()有7次时,执行线程

        for (int i = 0; i < 7; i++) {
            final int ti = i;
            new Thread(()->{
                System.out.println(ti);

                try {
                    c.await(); //等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

8.3 Semaphore

信号量

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

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

        for (int i = 0; i < 7; i++) {
            new Thread(()->{
                //acquire 得到
                try {
                    s.acquire();
                    System.out.println(Thread.currentThread().getName()+" in");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+" out");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //release 释放
                    s.release();
                }
            }).start();
        }
    }
}
/**
* 3个资源,
* acquire请求获取一个资源,如没有,阻塞,直到有可用的资源 (信号量-1)
* release释放资源 (信号量+1)
*/

9.读写锁

写与写之间互斥

读与写之间互斥

读与读之间不互斥

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

public class TestReadWriteLock {
    public static void main(String[] args) {
        Cache c = new Cache();
        for (int i = 0; i < 5; i++) {
            final int it = i;
            new Thread(()->{
                c.put(""+it,""+it);
            },"w"+it).start();
        }
        for (int i = 0; i < 10; i++) {
            final int it = i;
            new Thread(()->{
                c.get(""+it);
            },"r"+it).start();
        }
    }
}

class Cache{

    private volatile Map<String,String> map = new HashMap<>();
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void put(String key, String value){
        try{
            readWriteLock.writeLock().lock(); //写锁 lock
            System.out.println(Thread.currentThread().getName()+" write \""+key+":"+value+"\"");
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" write complete");
        } finally {
            readWriteLock.writeLock().unlock(); //写锁 unlock
        }
    }
    public void get(String key){
        try{
            readWriteLock.readLock().lock(); //读锁 lock
            System.out.println(Thread.currentThread().getName()+" read "+key);
            map.get(key);
            System.out.println(Thread.currentThread().getName()+" read complete");
        } finally {
            readWriteLock.readLock().unlock(); //读锁 unlock
        }
    }
}

10.阻塞队列

Collection
Set
Queue
List
Deque
BlockingQueue
AbstractQueue
LinkedBlockingQueue
ArrayBlockingQueue

Deque 为双端队列

使用队列

添加,移出

四组API

有返回值,抛出异常有返回值,不抛出异常等待,阻塞超时等待
添加add(obj)offer(obj)put(obj)offer(obj, timeout, timeUnit)
移除remove()poll()take()poll(timeout, timeUnit)
队首元素element()peek()

take, put方法线程安全


SynchronousQueue

同步队列,put,take

A blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa. A synchronous queue does not have any internal capacity, not even a capacity of one. You cannot peek at a synchronous queue because an element is only present when you try to remove it; you cannot insert an element (using any method) unless another thread is trying to remove it; you cannot iterate as there is nothing to iterate. The head of the queue is the element that the first queued inserting thread is trying to add to the queue; if there is no such queued thread then no element is available for removal and poll() will return null. For purposes of other Collection methods (for example contains), a SynchronousQueue acts as an empty collection. This queue does not permit null elements.

put和take匹配,

11.线程池

池化技术 -> 优化资源使用:提前准备好资源

线程池,连接池,内存池,对象池…

线程池好处:

  1. 降低资源的消耗
  2. 提高响应速度
  3. 方便管理

线程复用,可以控制最大并发数,管理线程


三大操作


ExecutorService pool = Executors.newSingleThreadExecutor();
// pool = Executors.newFixedThreadPool(int);
// pool = Executors.newCachedThreadPool();

pool.execute(Runnable);

pool.shutdown();

七大参数

//源码,本质是返回ThreadPoolExecutor对象
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      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 newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
}
//源码,七大参数
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
                              int maximumPoolSize,//最大线程池大小
                              long keepAliveTime, //超时 没有调用释放
                              TimeUnit unit, //超时单位
                              BlockingQueue<Runnable> workQueue, //阻塞队列
                              ThreadFactory threadFactory, //线程工程,创建线程
                              RejectedExecutionHandler handler) //拒绝策略
//最大承载线程:maximumPoolSize + workQueue.size();
ExecutorService pool = new ThreadPoolExecutor(2,
                                             5,
                                             0,
                                             TimeUnit.MILLISECONDS,
                                             new LinkedBlockingQueue<>(3),
                                             Executors.defaultThreadFactory,
                                             new ThreadPoolExecutor.AbortPolicy());
/**4种拒绝策略
* 1. ThreadPoolExecutor.AbortPolicy 丢弃,抛出异常
* 2. ThreadPoolExecutor.CallerRunsPolicy 拒绝,由提交的线程执行任务
* 3. ThreadPoolExecutor.DiscardPolicy 丢弃,不抛出异常
* 4. ThreadPoolExecutor.DiscardOldestPolicy 丢弃队列最前面的任务,重新提交被拒绝的任务
*/

最大线程池大小的定义

1.CPU密集型,定义为CPU核数 Runtime.getRuntime().availableProcessors();

2.IO密集型,定义为程序中十分耗IO的线程数+1

12.四大函数式接口

Lamda表达式,链式编程,函数式接口,Stream流式计算


四大函数式接口:位于java.util.function包下

  1. Consumer<T> void accept(T t) (消费型接口,有参数,无返回)
  2. Function<T,R> R apply(T t) (函数型接口,有参数,有返回)
  3. Predicate<T> boolean test(T t) (断定型接口,有参数,返回boolean)
  4. Supplier<T> T get() (供给型接口,无参数,有返回)

13.Stream流式计算

什么是Stream流式计算

import java.util.*;

public class Test {
    public static void main(String[] args) {
        User u1 = new User(1,"a",21);
        User u2 = new User(2,"b",22);
        User u3 = new User(3,"c",23);
        User u4 = new User(4,"d",24);
        User u5 = new User(6,"e",25);
        List<User> list = Arrays.asList(u1,u2,u3,u4,u5);
        /**筛选用户
         * 1.ID为偶数
         * 2.年龄大于23
         * 3.用户名转为大写
         * 4.用户名倒序
         * 5.只输出一个用户
         */
        list.stream()
                .filter(u->u.getId()%2==0) //filter 参数: Predicate<? super T> predicate
                .filter(u->u.getAge()>23)
                .map(u->u.getName().toUpperCase())// map 参数: Function<? super T, ? extends R> mapper
                .sorted(Comparator.reverseOrder())//sorted 参数: Comparator<? super T> comparator
                .limit(1) // limit 参数: long maxSize
                .forEach(System.out::println); //forEach 参数: Consumer<? super T> action
    }
}

class User{
    private int id;
    private String name;
    private int age;

    public User(int id, String name, int age){
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public void setId(int id){
        this.id = id;
    }
    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public int getId(){
        return id;
    }
    public String getName(){
        return name;
    }
    public int getAge(){
        return age;
    }
}

14. ForkJoin

将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出

ForkJoin特点:工作窃取(将小任务加到线程队列中,当没有任务执行时,再从一个随机线程的队列中偷一个并把它放在自己的队列中)

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

public class TestForkJoin {
    public static void test1(long start,long end){
        long t1 = System.currentTimeMillis();

        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> t = new Cal(start,end);
        long sum = pool.invoke(t);

        long t2 = System.currentTimeMillis();
        System.out.println("sum="+sum+", cost:"+(t2-t1)+"ms");
    }
    public static void test2(long start,long end){
        long t1 = System.currentTimeMillis();

        long sum = LongStream.rangeClosed(start,end).parallel().reduce(0,Long::sum);

        long t2 = System.currentTimeMillis();
        System.out.println("sum="+sum+", cost:"+(t2-t1)+"ms");
    }
    public static void main(String[] args) {
        long start = 1L;
        long end = 10_0000_0000L;
        test1(start,end);
        test2(start,end);
    }
}

class Cal extends RecursiveTask<Long>{

    private long start;
    private long end;
    private long temp = 1000L;
    public Cal(long start,long end){
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if(end-start<temp){
            long sum = 0;
            for(long i=start;i<=end;i++){
                sum += i;
            }
            return sum;
        }
        long mid = (start+end)/2;
        Cal left = new Cal(start,mid);
        Cal right = new Cal(mid+1,end);
        left.fork();
        right.fork();
        return left.join()+right.join();
    }
}

15.异步回调

CompletableFuture<Void> c = CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("asyn");
        });

        c.get();

16.JMM

volatile

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

1.保证可见性

2.不保证原子性

3.禁止指令重排

JMM

java内存模型

关于JMM的一些同步约定:

​ 1.线程解锁前,必须把共享变量立刻刷回主存

​ 2.线程加锁前,必须读取主存中的最新值到工作内存中

​ 3.加锁和解锁是同一把锁

    • lock
    • unlock
    • read
    • load
    • use
    • assign
    • store
    • write

java.util.concurrent.atomic包下原子封装类保证操作原子性


指令重排,编译器优化的结果

对单线程,指令重排导致程序结果正确

对多线程,指令重排可能导致程序结果不正确


内存屏障

17.单例模式

  • 饿汉式
  • 懒汉式
  • 静态内部类

18.CAS

CAS: Compare And Swap

import java.util.concurrent.atomic.AtomicInteger;

public class Test {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(20);
        atomicInteger.compareAndSet(20,21);
        System.out.println(atomicInteger.get());
    }
}

ABA问题:变量值由A变为B,再变为A,CAS将检测到变量的值未变化,实际上变化了

原子引用解决ABA问题(版本号)

AtomicStampedReference<Integer> atomicInteger 
    = new AtomicStampedReference<>(initialRef, //值
                                   initialStamp);  //版本号
// compareAndSet(期望值,新值,期望版本号,新版本号)

所有相同类型的包装类对象之间的比较,全部用equals方法,

说明:Integer var = ?在-128~127之间的赋值,Integer对象是在IntegeCache.cache产生,会复用已有的对象,这个区间的Integer可以直接使用==判断,这个区间之外的数据,都会在堆中产生,不会复用已有的对象

19.锁

19.1 公平锁,非公平锁

public ReentrantLock() {
        sync = new NonfairSync(); //默认非公平锁
    }

public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

19.2 可重入锁

已获取锁的线程再次获取该锁不会造成死锁

lock锁使用必须配对 lock.loc() … lock.unlock()

lock.lock();

lock.lock();

//...

lock.unlock();

lock.unlock();

19.3 自旋锁 (Spinlock)

当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环

19.4 死锁

排查 使用JDK/bin下工具

  1. jps -l 定位进程号
  2. jstack 进程号
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值