当前位置: 首页 > news >正文

JUC并发编程

1.synchonized

锁方法的话,锁的对象是方法的调用者。

锁方法:

public synchronized void  sale() {}

锁方法快:


synchronized(this) {} 

/
synchronized(Tick.class) {} 

锁方法:

package old;

public class SynchronizedTest {
    public static void main(String[] args) {
        //并发,多个线程操作同一个支援类
        Tick tick = new Tick();
        new Thread(() -> {
            for (int i = 1; i < 50; i++) {
                tick.sale();
            }
        }, "喜羊羊").start();
        new Thread(() -> {
            for (int i = 1; i < 50; i++) {
                tick.sale();
            }
        }, "美羊羊").start();
        new Thread(() -> {
            for (int i = 1; i < 50; i++) {
                tick.sale();
            }
        }, "懒羊羊").start();
    }
}

//票类
class Tick {
    private int tick = 50;

    public synchronized void  sale() {
        if (tick > 0) {
            System.out.println(Thread.currentThread().getName() + "抢到了第:" + tick + "票,还剩余:" + (tick--) + "张票");
        }
    }
}

结果:

2.Lock 

分为可重入锁,可重入读锁,可重入写锁

  • ReentrantLock , ReentrantReadWriteLock.ReadLock , ReentrantReadWriteLock.WriteLock

可重入锁

 公平锁:十分公平,可以先来后到    3h   3s

 非公平锁:十分不公平,可以先来后到(默认)

package old;

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

public class LockTest {
    public static void main(String[] args) {
        //并发,多个线程操作同一个资源类
        Tick2 tick = new Tick2();
        new Thread(() -> {for (int i = 1; i < 50; i++) tick.sale();}, "喜羊羊").start();
        new Thread(() -> {for (int i = 1; i < 50; i++) tick.sale();}, "美羊羊").start();
        new Thread(() -> {for (int i = 1; i < 50; i++) tick.sale();}, "懒羊羊").start();
    }
}

/**
 * Lock三部曲
 * 1.new ReentrantLock
 * 2.lock.lock();
 *3.finally--> {lock.unlock();} 解锁
 */
//票类
class Tick2 {
    private int tick = 50;
    Lock lock = new ReentrantLock();


    public void sale() {
        lock.lock();

        try {
            if (tick > 0) {
                System.out.println(Thread.currentThread().getName() + "抢到了第:" + tick + "票,还剩余:" + (tick--) + "张票");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

3.synchonized与lock的区别

 4.synchonized(等待,通信,生产者消费者问题)

this.wait();

this.notify();

this.notifyAll();

package example;

/**
 * 线程间的通信问题:生产者与消费者问题,等待唤醒,通知唤醒
 * A :num++
 * B :num--
 */
public class A {

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

/**
 * 判断等待,业务,通知
 */
class Data { //数据  资源类
    private int number = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
        if (number != 0) {
            //等待
            this.wait();
        }
        //业务
        number++;
        System.out.println(Thread.currentThread().getName() + "生产了,剩余数量为" + number);
        //通知
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
        if (number == 0) {
            //等待
            this.wait();
        }
        //业务
        number--;
        System.out.println(Thread.currentThread().getName() + "消费了,数量为" + number);
        //通知
        this.notifyAll();
    }
}

问题存在,ABCD四个线程

 改为while就好了,解释一下,就是我们的if判断的话,判断一次,if的条件成立就进去循环了。而用while的话,while的条件一修改就会等待

package example;

/**
 * 线程间的通信问题:生产者与消费者问题,等待唤醒,通知唤醒
 * A :num++
 * B :num--
 */
public class A {

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

}


/**
 * 判断等待,业务,通知
 */
class Data { //数据  资源类
    private int number = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
        while (number != 0) {
            //等待
            this.wait();
        }
        //业务
        number++;
        System.out.println(Thread.currentThread().getName() + "生产了,剩余数量为" + number);
        //通知
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
        while (number == 0) {
            //等待
            this.wait();
        }
        //业务
        number--;
        System.out.println(Thread.currentThread().getName() + "消费了,数量为" + number);
        //通知
        this.notifyAll();
    }
}

结果:

  5.Lock(等待,通信,生产者消费者问题)

condition.await();

condition.signal();

condition.signalAll();

package example;

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

/**
 * 线程间的通信问题:生产者与消费者问题,等待唤醒,通知唤醒
 * A :num++
 * B :num--
 */
public class B {

    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        }, "D").start();

    }
}


/**
 * 判断等待,业务,通知
 */
class Data2 { //数据  资源类
    private int number = 0;

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    //+1
    public void increment() {
        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
    public void decrement() {

        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();
        }

    }
}

结果:

 精准唤醒:

package example;


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

/**
 * 线程间的通信问题:生产者与消费者问题,等待唤醒,通知唤醒
 * A :num++
 * B :num--
 */
public class C {

    public static void main(String[] args) {
        Data3 data = new Data3();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printfA();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printfB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printfC();
            }
        }, "C").start();


    }
}


/**
 * 判断等待,业务,通知
 */
class Data3 { //数据  资源类
    private int number = 1;

    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();


    public void printfA() {
        lock.lock();
        try {
            while (number != 1) {
                //等待
                condition1.await();
            }
            //业务
            number = 2;
            System.out.println(Thread.currentThread().getName() + "AAAAAAAAA");
            //通知
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }


    public void printfB() {

        lock.lock();
        try {
            while (number != 2) {
                //等待
                condition2.await();
            }
            //业务
            number = 3;
            System.out.println(Thread.currentThread().getName() + "BBBBBBBB");
            //通知
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void printfC() {

        lock.lock();
        try {
            while (number != 3) {
                //等待
                condition3.await();
            }
            //业务
            number=1;
            System.out.println(Thread.currentThread().getName() + "CCCCCCCCC");
            //通知
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

结果:

 6.8锁问题

/**
 * 8锁,就是关于锁的8个问题
 * 1、标准情况下,两个线程先打印 发短信还是 打电话?1/发短信  2/打电话
 * 1、sendSms延迟4秒,两个线程先打印 发短信还是 打电话?1/发短信  2/打电话
 */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        //锁的存在
        new Thread(()->{
            phone.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.call();
        },"B").start();
    }
}
class Phone{

    // synchronized 锁的对象是方法的调用者!、
    // 两个方法用的是同一个锁,谁先拿到谁执行!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }
}
/**
 * 3、 增加了一个普通方法后!先执行发短信还是Hello? 普通方法
 * 4、 两个对象,两个同步方法, 发短信还是 打电话? // 打电话
 */
public class Test2  {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁!
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();
        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
class Phone2{

    // synchronized 锁的对象是方法的调用者!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
    // 这里没有锁!不是同步方法,不受锁的影响
    public void hello(){
        System.out.println("hello");
    }
}
/**
 * 5、增加两个静态的同步方法,只有一个对象,先打印 发短信?打电话?
 * 6、两个对象!增加两个静态的同步方法, 先打印 发短信?打电话?
 */
public class Test3  {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是Class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
// Phone3唯一的一个 Class 对象
class Phone3{

    // synchronized 锁的对象是方法的调用者!
    // static 静态方法
    // 类一加载就有了!锁的是Class
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public static synchronized void call(){
        System.out.println("打电话");
    }
}
/**
 * 1、1个静态的同步方法,1个普通的同步方法 ,一个对象,先打印 发短信?打电话?
 * 2、1个静态的同步方法,1个普通的同步方法 ,两个对象,先打印 发短信?打电话?
 */
public class Test4  {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是Class
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
// Phone4唯一的一个 Class 对象
class Phone4{

    // 静态的同步方法 锁的是 Class 类模板
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    // 普通的同步方法  锁的调用者
    public synchronized void call(){
        System.out.println("打电话");
    }
}

7.集合不安全

List不安全

我们来看一下List这个集合类:

//java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {
    public static void main(String[] args) {

        List<Object> arrayList = new ArrayList<>();

        for(int i=1;i<=10;i++){
            new Thread(()->{
                arrayList.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }

    }
}

会造成:

ArrayList 在并发情况下是不安全的!

解决方案:

1、切换成Vector就是线程安全的啦!

 2、使用Collections.synchronizedList(new ArrayList<>());

public class ListTest {
    public static void main(String[] args) {

        List<Object> arrayList = Collections.synchronizedList(new ArrayList<>());

        for(int i=1;i<=10;i++){
            new Thread(()->{
                arrayList.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }

    }
}

3、使用JUC中的包:List arrayList = new CopyOnWriteArrayList<>();

public class ListTest {
    public static void main(String[] args) {

        List<Object> arrayList = new CopyOnWriteArrayList<>();

        for(int i=1;i<=10;i++){
            new Thread(()->{
                arrayList.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }

    }
}

CopyOnWriteArrayList:写入时复制! COW 计算机程序设计领域的一种优化策略

多个线程调用的时候,list,读取的时候,固定的,写入(存在覆盖操作);在写入的时候避免覆盖,造成数据错乱的问题;

CopyOnWriteArrayList比Vector厉害在哪里?

Vector底层是使用synchronized关键字来实现的:效率特别低下。

CopyOnWriteArrayList使用的是Lock锁,效率会更加高效!

 Set不安全

和List、Set同级的还有一个BlockingQueue 阻塞队列;

Set和List同理可得: 多线程情况下,普通的Set集合是线程不安全的;

解决方案还是两种:

  • 使用Collections工具类的synchronized包装的Set类
  • 使用CopyOnWriteArraySet 写入复制的JUC解决方案
//同理:java.util.ConcurrentModificationException
// 解决方案:
public class SetTest {
    public static void main(String[] args) {
//        Set<String> hashSet = Collections.synchronizedSet(new HashSet<>()); //解决方案1
        Set<String> hashSet = new CopyOnWriteArraySet<>();//解决方案2
        for (int i = 1; i < 100; i++) {
            new Thread(()->{
                hashSet.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(hashSet);
            },String.valueOf(i)).start();
        }
    }
}

HashSet底层是什么?

hashSet底层就是一个HashMap

他把value放到了hashmap的key,把hashmap的value用常量代替了

public HashSet() {
        map = new HashMap<>();
}

//add 本质其实就是一个map的key,map的key是无法重复的,所以使用的就是map存储
//hashSet就是使用了hashmap key不能重复的原理
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
}
//PRESENT是什么? 是一个常量  不会改变的常量  无用的占位
private static final Object PRESENT = new Object();

Map不安全

回顾map的基本操作:

//map 是这样用的吗?  不是,工作中不使用这个
//默认等价什么? new HashMap<>(16,0.75);
Map<String, String> map = new HashMap<>();
//加载因子、初始化容量

同样的HashMap基础类也存在并发修改异常

public static void main(String[] args) {
        //map 是这样用的吗?  不是,工作中不使用这个
        //默认等价什么? new HashMap<>(16,0.75);
        Map<String, String> map = new HashMap<>();
        //加载因子、初始化容量
        for (int i = 1; i < 100; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }

结果同样的出现了:异常java.util.ConcurrentModificationException 并发修改异常

解决方案:

  • 使用Collections.synchronizedMap(new HashMap<>());处理
  • 使用ConcurrentHashMap进行并发处理

底层原理:CAS+synchonized,就是比较并替换加同步锁

8.Callable(简单)

 1、可以有返回值;
2、可以抛出异常;
3、方法不同,run()/call()

代码测试

传统使用线程方式:

public class CallableTest {
    public static void main(String[] args) {
        for (int i = 1; i < 10; i++) {
            new Thread(new MyThread()).start();
        }
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

使用Callable进行多线程操作:

Calleable 泛型T就是call运行方法的返回值类型

但是如何使用呢?

Callable怎么放入到Thread里面呢?

在这里插入图片描述

对于Thread运行,只能传入Runnable类型的参数;

我们这是Callable 怎么办呢?

看JDK api文档:

在Runnable里面有一个叫做FutureTask的实现类,我们进去看一下。

 FutureTask中可以接受Callable参数;

 这样我们就可以先把Callable 放入到FutureTask中, 如何再把FutureTask 放入到Thread就可以了。

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        for (int i = 1; i < 10; i++) {
//            new Thread(new Runnable()).start();
//            new Thread(new FutureTask<>( Callable)).start();
            MyThread thread= new MyThread();
            //适配类:FutureTask
            FutureTask<String> futureTask = new FutureTask<>(thread);
            //放入Thread使用
            new Thread(futureTask,String.valueOf(i)).start();
            //获取返回值
            String s = futureTask.get();
            System.out.println("返回值:"+ s);
        }
    }
}

class MyThread implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("Call:"+Thread.currentThread().getName());
        return "String"+Thread.currentThread().getName();
    }
}

这样我们就可以使用Callable来进行多线程编程了,并且我们发现可以有返回值,并且可以抛出异常。

 注意两个重点:

//结果会被缓存,只进行其中一次线程操作,返回一个结果 
new Thread(futureTask, "A").start();
new Thread(futureTask, "B").start();


  String s = futureTask.get();
//线程运行可能有耗时间操作,获取返回结果可能会很久,我们尽量最后在获取返回结果,或者使用异步通信

9.常用的辅助类(必会!)

 CountDownLatch

 其实就是一个减法计数器,对于计数器归零之后再进行后面的操作,这是一个计数器!

//这是一个计数器  减法
public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        //总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6 ; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" Go out");
                countDownLatch.countDown(); //每个线程都数量-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await();  //等待计数器归零  然后向下执行

        System.out.println("close door");

    }

}

主要方法:

  • countDown 减一操作;
  • await 等待计数器归零。

await等待计数器为0,就唤醒,再继续向下运行。

CyclickBarrier

 其实就是一个加法计数器;

public class CyclicBarrierDemo {
    public static void main(String[] args) {

        //主线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙~");
        });

        for (int i = 1; i <= 7; i++) {
            //子线程
            int finalI = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 收集了第 {"+ finalI+"} 颗龙珠");
                try {
                    cyclicBarrier.await(); //加法计数 等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }

    }
}

Semaphore

Semaphore:信号量

抢车位:

3个车位 6辆车:

public class SemaphoreDemo {
    public static void main(String[] args) {
        //停车位为3个
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; i++) {
            int finalI = i;
            new Thread(()->{
                try {
                    semaphore.acquire(); //得到
                    //抢到车位
                    System.out.println(Thread.currentThread().getName()+" 抢到了车位{"+ finalI +"}");
                    TimeUnit.SECONDS.sleep(2); //停车2s
                    System.out.println(Thread.currentThread().getName()+" 离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();//释放
                }
            },String.valueOf(i)).start();
        }
    }
}

原理:

semaphore.acquire()获得资源,如果资源已经使用完了,就等待资源释放后再进行使用!

semaphore.release()释放,会将当前的信号量释放+1,然后唤醒等待的线程!

作用: 多个共享资源互斥的使用! 并发限流,控制最大的线程数!

10.读写锁

先对于不加锁的情况:

如果我们做一个我们自己的cache缓存。分别有写入操作、读取操作;

我们采用五个线程去写入,使用十个线程去读取。

我们来看一下这个的效果,如果我们不加锁的情况!

package com.ogj.rw;

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

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache_ReadWriteLock mycache = new MyCache_ReadWriteLock();
        //开启5个线程 写入数据
        for (int i = 1; i <=5 ; i++) {
            int finalI = i;
            new Thread(()->{
                mycache.put(String.valueOf(finalI),String.valueOf(finalI));
            }).start();
        }
        //开启10个线程去读取数据
        for (int i = 1; i <=10 ; i++) {
            int finalI = i;
            new Thread(()->{
                String o = mycache.get(String.valueOf(finalI));
            }).start();
        }
    }
}

class MyCache_ReadWriteLock{
    private volatile Map<String,String> map=new HashMap<>();

    //使用读写锁
    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
    //普通锁
    private Lock lock=new ReentrantLock();

    public void put(String key,String value){
        //写入
        System.out.println(Thread.currentThread().getName()+" 线程 开始写入");
        map.put(key, value);
        System.out.println(Thread.currentThread().getName()+" 线程 写入OK");
    }

    public String get(String key){
        //得到
        System.out.println(Thread.currentThread().getName()+" 线程 开始读取");
        String o = map.get(key);
        System.out.println(Thread.currentThread().getName()+" 线程 读取OK");
        return o;
    }
}

结果:

Thread-0 线程 开始写入
Thread-4 线程 开始写入  # 插入了其他的线程进行写入
Thread-4 线程 写入OK
Thread-3 线程 开始写入
Thread-1 线程 开始写入
Thread-2 线程 开始写入
Thread-1 线程 写入OK
Thread-3 线程 写入OK
Thread-0 线程 写入OK   # 对于这种情况会出现 数据不一致等情况
Thread-2 线程 写入OK
Thread-5 线程 开始读取
Thread-6 线程 开始读取
Thread-6 线程 读取OK
Thread-7 线程 开始读取
Thread-7 线程 读取OK
Thread-5 线程 读取OK
Thread-8 线程 开始读取
Thread-8 线程 读取OK
Thread-9 线程 开始读取
Thread-9 线程 读取OK
Thread-10 线程 开始读取
Thread-11 线程 开始读取
Thread-12 线程 开始读取
Thread-12 线程 读取OK
Thread-10 线程 读取OK
Thread-14 线程 开始读取
Thread-13 线程 开始读取
Thread-13 线程 读取OK
Thread-11 线程 读取OK
Thread-14 线程 读取OK

Process finished with exit code 0

所以如果我们不加锁的情况,多线程的读写会造成数据不可靠的问题。

我们也可以采用synchronized这种重量锁和轻量锁 lock去保证数据的可靠。

但是这次我们采用更细粒度的锁:ReadWriteLock 读写锁来保证

package com.ogj.rw;

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

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache_ReadWriteLock mycache = new MyCache_ReadWriteLock();
        //开启5个线程 写入数据
        for (int i = 1; i <=5 ; i++) {
            int finalI = i;
            new Thread(()->{
                mycache.put(String.valueOf(finalI),String.valueOf(finalI));
            }).start();
        }
        //开启10个线程去读取数据
        for (int i = 1; i <=10 ; i++) {
            int finalI = i;
            new Thread(()->{
                String o = mycache.get(String.valueOf(finalI));
            }).start();
        }
    }
}

class MyCache_ReadWriteLock{
    private volatile Map<String,String> map=new HashMap<>();

    //使用读写锁
    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
    //普通锁
    private Lock lock=new ReentrantLock();

    public void put(String key,String value){
        //加锁
        readWriteLock.writeLock().lock();
        try {
            //写入
            //业务流程
            System.out.println(Thread.currentThread().getName()+" 线程 开始写入");
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+" 线程 写入OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock(); //解锁
        }
    }

    public String get(String key){
        //加锁
        String o="";
        readWriteLock.readLock().lock();
        try {
            //得到
            System.out.println(Thread.currentThread().getName()+" 线程 开始读取");
            o = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 线程 读取OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
        return o;
    }
}

运行结果如下:

Thread-0 线程 开始写入
Thread-0 线程 写入OK
Thread-1 线程 开始写入
Thread-1 线程 写入OK
Thread-2 线程 开始写入
Thread-2 线程 写入OK
Thread-3 线程 开始写入
Thread-3 线程 写入OK
Thread-4 线程 开始写入
Thread-4 线程 写入OK

# 以上 整个过程没有再出现错乱的情况,对于读取,我们运行多个线程同时读取,
# 因为这样不会造成数据不一致问题,也能在一定程度上提高效率
Thread-9 线程 开始读取
Thread-9 线程 读取OK
Thread-10 线程 开始读取
Thread-5 线程 开始读取
Thread-11 线程 开始读取
Thread-11 线程 读取OK
Thread-10 线程 读取OK
Thread-7 线程 开始读取
Thread-7 线程 读取OK
Thread-6 线程 开始读取
Thread-5 线程 读取OK
Thread-14 线程 开始读取
Thread-8 线程 开始读取
Thread-14 线程 读取OK
Thread-6 线程 读取OK
Thread-13 线程 开始读取
Thread-12 线程 开始读取
Thread-13 线程 读取OK
Thread-8 线程 读取OK
Thread-12 线程 读取OK

11.阻塞队列

阻塞

队列

 阻塞队列jdk1.8文档解释:

BlockingQueue

blockingQueue 是Collection的一个子类;

什么情况我们会使用 阻塞队列呢?

多线程并发处理、线程池!

整个阻塞队列的家族如下:Queue以下实现的有Deque、AbstaractQueue、BlockingQueue;

BlockingQueue以下有Link链表实现的阻塞队列、也有Array数组实现的阻塞队列

如何使用阻塞队列呢?

操作:添加、移除

但是实际我们要学的有:

四组API

  

相关文章:

  • Spring注解驱动系列总结
  • 记一次mysql 命令行登录报错(error while loading shared libraries: libssl.so.1.1)
  • PyTorch中DataLoader及其与enumerate()用法介绍
  • mac 使用 PyQt5 和 py_designer 搭建窗体
  • 嵌入式SQL开发
  • 对于HTTP协议,什么是长连接和短连接?
  • ReentrantLock读写锁
  • leetcode 1680. Concatenation of Consecutive Binary Numbers(连接连续的二进制数)
  • Python数据分析之时间序列的处理
  • 【PHP】如何搭建服务器环境 原生篇 | Ubuntu 18.04 + PHP8.1 + MySQL5.7 + Nginx 1.4
  • 【c语言】数据在内存中的存储
  • 数据结构考试必须要掌握的重点知识
  • 进程管理4——进程优先级
  • 外网访问内网80端口【内网穿透】
  • Android性能优化技术,在大厂中为何这么看重?进大厂必学好
  • 深入了解以太坊
  • .pyc 想到的一些问题
  • [译]CSS 居中(Center)方法大合集
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • 【笔记】你不知道的JS读书笔记——Promise
  • 0基础学习移动端适配
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • CentOS 7 防火墙操作
  • Codepen 每日精选(2018-3-25)
  • isset在php5.6-和php7.0+的一些差异
  • JavaScript中的对象个人分享
  • PHP的类修饰符与访问修饰符
  • 阿里云应用高可用服务公测发布
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 飞驰在Mesos的涡轮引擎上
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 如何在招聘中考核.NET架构师
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (利用IDEA+Maven)定制属于自己的jar包
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (一)u-boot-nand.bin的下载
  • (转)Scala的“=”符号简介
  • (转载)Linux 多线程条件变量同步
  • . Flume面试题
  • . NET自动找可写目录
  • .NET Standard 的管理策略
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .net开发引用程序集提示没有强名称的解决办法
  • .net知识和学习方法系列(二十一)CLR-枚举
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • [04]Web前端进阶—JS伪数组
  • [2010-8-30]
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——