JAVA进阶JUC并发编程(一)

进阶JUC

JUC是与属于三个包

java.util.concurrent
java.util.concurrent.atomic
java.util.concurrentlocks

回顾知识

  1. 线程和进程

    进程:一个程序。例子:QQ.exe程序的集合。

    线程:作为独立运行和独立调度的基本单位。例子:开一个进程Typora,写字,保存(线程负责)

    一个进程往往可以包含多个线程,至少包含一个!

    线程就是一个单独的资源类,没有任何附属的操作!

    Java默认有2个线程,是main/GC(垃圾回收)

    java真的可以开启线程吗?开不了,它是调用本地方法库(即是底层的C++、native)来开启的,java无法直接操作硬件

  2. 并发、并行

    并发:多线程操作同一个资源。(CPU一核,模拟出来多个线程,快速交替)

    并行:多个线程同时执行。(CPU多核,多个线程同时执行;线程池)

    补:

    Runtime.getRuntime(),availableProcessors();//获取CPU的核数

    并发编程:并发、并行

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

  3. 线程有几个状态

    NEW,新生

    RUNNABLE,运行

    BLOCKED,阻塞

    WAITING,等待(死死的等)

    TIMED_WAITING,超时等待

    TERMINATED,终止

  4. wait/sleep区别

    1. 来自不同的类

      wait=>Object

      sleep=>Thread

    2. 关于锁的释放

      wait会释放锁,sleep不会释放锁(抱着锁睡觉了)

    3. 使用的范围是不同的

      wait必须在同步代码块中

      sleep可以在任何地方

    4. 是否需要捕获异常

      wait不需要捕获异常

      sleep必须需要捕获异常

  5. Synchronized 和Lock区别

    1. Synchronized 内置的Java关键字,Lock是一个Java类

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

    3. Synchronized 会自动释放锁,Lock必须要手动释放锁!如果不释放锁,死锁

    4. Synchronized 线程1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去

    5. Synchronized 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,分公平(可以自己设置)

      注:

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

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

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

生产者和消费者问题

面试的:单例模式、排序算法、生产者和消费者、死锁

线程之间的通信问题,生产者和消费者问题!等待唤醒,通知唤醒

线程交替执行 A B操作同一个变量 num = 0

A num + 1

B num - 1

等待应该总是出现在循环中(注意点:防止虚假唤醒–>线程也可以唤醒,而不被通知,中断或超时)

JUC版的生产者和消费者问题:

通过Lock找到Condition(Synchronized–wait–notify---->Lock–await–signal)

Condition 精准的通知和唤醒线程

代码实现:

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

public class B{
     
    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.increment();
                } 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.increment();
                } catch (InterruptedException e){
     
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}

//判断等待,业务,通知
class Data{
     
    private int number = 0;
     
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    
    public void increment() throws InterruptedException{
     
        lock.lock();
        try{
     
            //业务代码
            while(number!=0){
     
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            condition.signalAll();
        } catch(Exception e){
     
            e.printStackTrace();
        } finally{
     
            lock.unlock();
        }
    }
    
     public void decrement() throws InterruptedException{
     
        lock.lock();
        try{
     
            //业务代码
            while(number==0){
     
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            condition.signalAll();
        } catch(Exception e){
     
            e.printStackTrace();
        } finally{
     
            lock.unlock();
        }
    }
}

八锁现象理解锁

  1. 标准情况下,两个线程先打印,先执行是发短信还是打电话?1–>发短信 2–>打电话

  2. 发短信延迟4s下,两个线程先打印,先执行是发短信还是打电话?1–>发短信 2–>打电话

    两个方法用的是同一个锁,谁先拿到谁执行!(Synchronized锁的对象是方法的调用者)

  3. 增加了一个普通方法后,先执行是发短信还是普通方法?1–>普通方法 2–>发短信

  4. 两个对象,两个同步方法,发短信延迟4s下,先执行是发短信还是打电话?1–>打电话 2–>发短信

  5. 增加两个静态的同步方法,只有一个对象,发短信延迟4s下,先执行是发短信还是打电话?1–>发短信 2–>打电话

  6. 两个对象,增加两个静态的同步方法,发短信延迟4s下,先执行是发短信还是打电话?1–>发短信 2–>打电话

    两个对象的Class类模板只有一个,static静态方法,锁的是Class

  7. 一个静态的同步方法,一个普通的同步方法,一个对象,先执行是发短信还是打电话?1–>打电话 2–>发短信

  8. 一个静态的同步方法,一个普通的同步方法,两个对象,先执行是发短信还是打电话?1–>打电话 2–>发短信

集合类不安全

并发下ArrayList不安全吗?答:不安全(会报:ConcurrentModificationException 并发修改异常)

解决方案:

  1. List list = new Vector<>();

  2. List list = Collections.synchronizedList(new ArrayList<>());

  3. List list = new CopyOnWriteArrayList<>());

    CopyOnWrite写入时复制,COW计算机程序设计领域的一种优化策略;

    多线程调用的时候,list读取的时候,在写入的时候避免覆盖,造成数据问题!

CopyOnWriteArrayList 比 Vector 厉害在哪里?

Vector 使用的是 Synchronized;CopyOnWriteArrayList 使用的是 Lock

并发下HashSet不安全吗?答:不安全

解决方案:

  1. Set set = Collections.synchronizedSet(new HashSet<>());
  2. Set set = new CopyOnWriteArraySet<>());

HashSet底层是什么?

HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变,此类允许使用null元素。
在HashSet中,元素都存到HashMap键值对的Key上面,而Value时有一个统一的值private static final Object PRESENT = new Object();,(定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。)

map是这样子用的吗?:不是,工作中不用HashMap

默认等价于什么?new HashMap<>(16,0.75)(加载因子、初始化容量(1<<30))

HashMap呢?

解决方案:

  1. Map set = Collections.synchronizedMap(new HashMap<>());
  2. Map set = new ConcurrentHashMap<>());

你可能感兴趣的:(JUC并发编程,Java进阶学习,并发编程,java,多线程,面试,设计模式)