Java JUC并发知识详解

今天夜里没啥事,想把java并发编程常用的JUC知识点都总结一点。我写博客开始的时间是2019/06/10 凌晨4:30,我就想说一句,你们见过凌晨四点的上海吗?

预计会写这几个方面:

1.谈谈对volatile的理解

2.CAS的介绍

3.ABA问题

4.线程安全的ArrayList的一些列解决方案

5.java常见的锁(公/非平锁,可重入锁,自旋锁)

6.CountDownLatch/CyclicBarrier/Semaphore

7.阻塞队列

8.线程池的详细原理介绍

可以说这些问题基本上大厂面试都会或多或少的有一点,那面就让我们进入正题吧。

1.谈谈对volatile的理解

详细看我这一篇博客的介绍https://blog.csdn.net/oldshaui/article/details/89342858

2-3.CAS的介绍和ABA的问题

可以看我的博客:https://blog.csdn.net/oldshaui/article/details/89432726

4.线程安全的ArrayList(set,map类似)的一些列解决方案

1.使用jdk1.0提供的vector(基本不用的方法)

2.使用Collections.synchronizedList(传入一个线程不安全的集合)(把list转换为一个安全的集合)

3.使用CopyOnWriteArrayList

介绍下CopyOnWriteArrayList

/**
     * 笔记
     * 写时复制 copyOnWrite 容器即写时复制的容器 往容器添加元素的时候,不直接往当前容器object[]添加,而是先将当前容器object[]进行
     * copy 复制出一个新的object[] newElements 然后向新容器object[] newElements 里面添加元素 添加元素后,
     * 再将原容器的引用指向新的容器 setArray(newElements);
     * 这样的好处是可以对copyOnWrite容器进行并发的读,而不需要加锁 因为当前容器不会添加任何容器.所以copyOnwrite容器也是一种
     * 读写分离的思想,读和写不同的容器.
     *          public boolean add(E e) {
     *         final ReentrantLock lock = this.lock;
     *         lock.lock();
     *         try {
     *             Object[] elements = getArray();
     *             int len = elements.length;
     *             Object[] newElements = Arrays.copyOf(elements, len + 1);
     *             newElements[len] = e;
     *             setArray(newElements);
     *             return true;
     *         } finally {
     *             lock.unlock();
     *         }
     *     }
     * @param args
     */

5.Java常见的几种锁

1.可重入锁

package cn.carryshuai.one.多线程.java的几种锁.可重入锁;
class Phone{
    public synchronized void sendSms() throws Exception{
        System.out.println(Thread.currentThread().getName()+"\tsendSms");
        sendEmail();
    }
    public synchronized void sendEmail() throws Exception{
        System.out.println(Thread.currentThread().getName()+"\tsendEmail");
    }

}
/**
 * Description:
 *  可重入锁(也叫做递归锁)
 *  指的是同一先生外层函数获得锁后,内层敌对函数任然能获取该锁的代码
 *  在同一线程外外层方法获取锁的时候,在进入内层方法会自动获取锁
 *
 *  也就是说,线程可以进入任何一个它已经标记的锁所同步的代码块
 *
 * @author [email protected]
 * @date 2019-06-10 23:36
 **/
public class ReenterLockDemo {
    /**
     * t1 sendSms
     * t1 sendEmail
     * t2 sendSms
     * t2 sendEmail
     * @param args
     */
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            try {
                phone.sendSms();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t1").start();
        new Thread(()->{
            try {
                phone.sendSms();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t2").start();
    }
}



//执行结果
t1	sendSms
t1	sendEmail
t2	sendSms
t2	sendEmail
 
 

2.非/公平锁

3.自旋锁

package cn.carryshuai.one.多线程.java的几种锁.自旋锁;


import java.util.concurrent.atomic.AtomicReference;

/**
 * 自旋锁:指定尝试获取锁的线程不会立即堵塞,而是采用循环的方式尝试获取锁,
 * 这样的好处就是减少了线程上下文的切换的消耗,缺点是循环会消耗cpu
 */
public class OptionalDemo {

    private AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public void lock(){
        System.out.println(Thread.currentThread() + " coming...");
        //如果期望是空的话,就设置为当前线程,跳出while循环
        while (!atomicReference.compareAndSet(null,Thread.currentThread())){

        }
    }
    public void unlock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(thread+"unlock;;;;;");
    }

    public static void main(String[] args) throws InterruptedException {
        OptionalDemo optionalDemo = new OptionalDemo();
        new Thread(()->{
            optionalDemo.lock();
            try {
                Thread.sleep(1000);
            }catch (Exception e){

            }
            System.out.println("hahah");
            optionalDemo.unlock();
        }).start();
        Thread.sleep(1000);
        new Thread(() -> {
            optionalDemo.lock();
            System.out.println("hehehe");
            optionalDemo.unlock();
        }).start();
    }
}

4.读写锁

package cn.carryshuai.one.多线程.java的几种锁.读写锁;

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

public class ReentrantReadWriteLockDemo {

    public static void main(String[] args){
        Mycache mycache = new Mycache();
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                try {
                    mycache.put(temp+"",temp+"");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                //读取数据
                try {
                    mycache.get(temp+"");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

//一个缓存类
class Mycache{

    private volatile Map<String,Object> map = new HashMap<>();
    //一个读写锁
    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    //提供一个写的操作方法
    public void put(String key,Object value) throws Exception {
        //添加写锁
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        try {
            writeLock.lock();
            System.out.println(Thread.currentThread().getName()+"正在写入:"+key);
            //模拟睡眠
            Thread.sleep(1000);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入完成---");

        }finally {
            writeLock.unlock();
        }
    }
    //提供一个读取的方法
    public void get(String key) throws Exception {
        //添加一个读锁
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        try {
            readLock.lock();
            System.out.println(Thread.currentThread().getName()+"正在读取---");
            //模拟睡眠
            Thread.sleep(1000);
            Object res = map.get(key);
            System.out.println(Thread.currentThread().getName() + " 读取完成,读取结果是 " + res);
        }finally {
            readLock.unlock();
        }
    }
}

6.CountDownLatch/CyclicBarrier/Semaphore

CountDownLatch

让一些线程堵塞直到另一个线程完成一系列操作后才被唤醒。CountDownLatch 主要有两个方法,当一个或多个线程调用 await 方法时,调用线程会被堵塞,其他线程调用 countDown 方法会将计数减一(调用 countDown 方法的线程不会堵塞),当计数其值变为零时,因调用 await 方法被堵塞的线程会被唤醒,继续执行。

假设我们有这么一个场景,教室里有班长和其他6个人在教室上自习,怎么保证班长等其他6个人都走出教室在把教室门给关掉。

public class CountDownLanchDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 离开了教室...");
            }, String.valueOf(i)).start();
        }
        System.out.println("班长把门给关了,离开了教室...");
    }
}

CyclicBarrier

举个集齐七龙珠,召唤神龙的例子

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        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()+"\t 收集到第"+ temp +"颗龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}
 

Semaphore

举个抢车位的例子

/**
 * Description
 *
 * @author [email protected]
 * @version 1.0
 * @date 2019-04-13 11:08
 **/
public class SemaphoreDemo {
    public static void main(String[] args) {
        //模拟3个停车位
        Semaphore semaphore = new Semaphore(3);
        //模拟6部汽车
        for (int i = 1; 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 停3秒离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放资源
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}
 

7.阻塞队列

阻塞队列的好处

核心方法:

8.线程池

线程池的好处:

线程池的七大参数:

线程池工作原理:

发布了62 篇原创文章 · 获赞 68 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/oldshaui/article/details/91359698