JUC——Day01

JUC Day01

文章总结自B站狂神说Java

JUC 是 java.util.concurrent以及下面的atomic和locks包的简称。称为并发编程,面试高频问。

1. 概念

1.1 进程与线程

进程是一个程序运行的集合,一个进程可以包含多个线程,至少包含一个线程,线程是进程最小的运行单位。在Java中最少包含两个线程:main、GC线程。
Java并不是真的可以开启线程,只能调用本地底层的C++方法,Java无法直接操作硬件设备。

1.2 并发与并行

并发为多个线程操作同一个资源,并行为多个线程同时执行(必须为多核CPU)。并发编程的本质是充分利用CPU的资源。

1.3 多线程状态

  1. NEW : 新生状态
  2. RUNABLE :运行状态
  3. BLOCKED:阻塞状态
  4. WATING :等待状态,死死的等待
  5. TIMED_WATING :超时等待
  6. TERMINATED :终止状态

1.3 wait 和 sleep

wait来自Object类;sleep来自Thread类;
wait会释放锁;sleep不会释放锁;
wait必须在同步代码块中使用;Sleep可以在任何地方使用;
wait不需要捕获异常;sleep必须捕获异常。

2.LOCK 锁

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

创建Lock对象:
在创建对象时可以传递参数来指定是否为公平锁:不穿参数默认为false:非公平锁:根据CPU来决定运行顺序;传递参数为true:公平锁:先来后到,等先来的线程运行完毕之后再运行后面的线程。

    Lock lock = new ReentrantLock(false);

加锁:
把需要家锁的代码放在try/catch代码块中,使用finally释放锁。

 public void sale(){
//      家锁
        lock.lock();
        try {
//             加锁之后的业务代码
            if (number > 0){
                System.out.println(Thread.currentThread().getName()+"卖出了"+number--+"------------->还剩"+number);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
        // 释放锁
            lock.unlock();
        }

    }

2.1传统的SynchronizedLock的区别:

  1. Synchornnized 是内置的Java关键字,Lock是一个关键字;
  2. Synchorniezd 无法判断锁的状态,Lock可以判断锁的状态;
  3. Synchornized 会自动的释放锁,Lock必须要手动释放锁,如果不释放有可能会出现死锁问题。
  4. Synchornized 如果线程A阻塞线程B就会一直等待线程A释放锁;Lock可以使用tryLock方法去尝试获取锁。
  5. Synchornized是可重入锁,不可以中断的,非公平的;Lcok 可重入锁,判断锁状态,自己设置公平或者非公平。
  6. Synchornized 适合锁少量的代码同步问题,Lock适合大量的锁同步代码。

2.2 LOCK锁的消费者与生产者问题

lock相较于synchornizednotifyAll方法变成了Condition对象的singalAll方法;wait变成了await方法。除去上述的区别外Condition还新增了对线程执行顺序控制的方法,对线程的精准通知唤醒。

juc解决生产者消费者问题

/**
 * @date 2020/7/26 13:28
 * juc 解决生产者消费者问题
 */
public class ProductAndCustomerJUC {

    public static void main(String[] args) {
        DataA dataA = new DataA();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    dataA.inc();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    dataA.dec();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    dataA.inc();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    dataA.dec();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class DataA{
    private int num = 0;

//    锁对象
    Lock lock = new ReentrantLock();

//    获取condition对象用来执行 await 和 signalAll 方法
    Condition condition = lock.newCondition();

    /**
     * 加 1 方法
     * @throws InterruptedException
     */
    public void inc() throws InterruptedException {
        try {
            lock.lock();
            while (num != 0){
//            相当于 synchronized 的 wait 方法。
                condition.await();
            }

            num++;
            System.out.println(Thread.currentThread().getName()+"=>"+num);

//        通知其它线程 相当于 synchronized 的 notifyAll 方法
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    /**
     * 减 1 方法
     * @throws InterruptedException
     */
    public void dec() throws InterruptedException {
        try {
            lock.lock();
            while (num == 0){
//            相当于 synchronized 的 wait 方法。
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"=>"+num);
//        通知其它线程 相当于 synchronized 的 notifyAll 方法
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

}

Condition 对线程的精准通知唤醒


/**
 * @date 2020/7/26 13:28
 * condition 对象精确唤醒线程
 */
public class ProductAndCustomerJUC02 {

    public static void main(String[] args) {
        DataB dataB = new DataB();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                dataB.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                dataB.printB();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                dataB.printC();
            }
        },"C").start();
    }
}

class DataB{
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private int num =  1;


    public void printA(){
        lock.lock();
        try {
//          业务代码
            while (num != 1){
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"-------------->printA");
            num = 2;
//            精准唤醒
            condition1.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {
//          业务代码
            while (num != 2){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"----------------->printB");
            num = 3;
//            精准唤醒
            condition2.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {
//          业务代码
            while (num != 3){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"-------------->printC");
            num = 1;
//            精准唤醒
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

}

2.2 锁的问题

  1. 没有锁的线程的运行顺序与调用顺序无关;
  2. 有锁的与休眠时间无关;
  3. synchornized锁的是调用方法的对象,也就是说如果一个对象调用两个方法,先拿到锁(先被调用就先拿到锁)的就先执行。
  4. 没有同步的方法不受锁的影响。
  5. 两个不同的对象调用同步的方法,不受对方锁的影响。
  6. 加了static标识符的方法锁的是全局唯一的class对象,即使是两个对象调用方法也是同把锁。
  7. 如果同时存在静态同步方法和普通同步方法,这个两个方法的锁的对象是不同的,所以不需要等待对方释放锁。

3. 不安全类

3.1 在并发下list并不安全

如果使用多个线程去往list中添加数据会java.util.ConcurrentModificationException异常,这个错误为并发修改异常。

public class ListDemo01 {
    public static void main(String[] args) {
        List<String> lists = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                lists.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(lists);
            },String.valueOf(i)).start();
        }
    }
}

解决方案:

  1. List<String> lists = new Vector<>();
  2. List<String> lists = Collections.synchronizedList(new ArrayList<>());
  3. List<String> lists = new CopyOnWriteArrayList<>();
    推荐使用第三个,实现原理是在读写分离,在写入时避免覆盖而造成的数据问题;相较于Vector而言它的add方法并不是使用的synchornized锁同步的方法,效率高。

3.2 在并发下set也不安全

在多线程共同像set添加内容时,set也会出现与list一样的异常List<String> lists = new CopyOnWriteArrayList<>();
解决方案:

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

HashSet的底层就是HashMap。

3.3 Map也不是安全的

出现的问题与上面的set和list的问题一样,推荐的解决方案是:
Map<String,Sting> map = new ConncurrnetHashMap<>();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值