UC并发编程学习笔记(简单易懂)3

简介: UC并发编程学习笔记(简单易懂)

5:读写锁(ReadWriteLock)

9fe4732a00104710ad830cbe2e3199f7.png

5.1 使用

package main;
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.ReentrantReadWriteLock;
/*
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁) 多个线程可以同时占有
* ReadWriteLock
* 读 - 读 可以共存
* 读 - 写 不能共存
* 写 - 写 不能共存
* */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        //MyCache myCache =new MyCache();
        MyCacheLock myCache =new MyCacheLock();
        //写入
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }
        //读取
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}
class MyCacheLock{
    private volatile Map<String,Object> map = new HashMap<>();
    //读写锁:更加细粒度的控制
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    //存,写 的时候,只希望同时只有一个线程写
    public  void  put(String key,Object value){
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }
    //取,读  所有人都可以读
    public  void  get(String key){
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}
class MyCache{
    private volatile Map<String,Object> map = new HashMap<>();
    //存,写
    public  void  put(String key,Object value){
        System.out.println(Thread.currentThread().getName()+"写入"+key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"写入OK");
    }
    //取,读
    public  void  get(String key){
        System.out.println(Thread.currentThread().getName()+"读取"+key);
        map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取OK");
    }
}
打印结果:
1写入1
1写入OK
2写入2
2写入OK
3写入3
3写入OK
4写入4
4写入OK
5写入5
5写入OK
2读取2
2读取OK
3读取3
3读取OK
5读取5
5读取OK
1读取1
4读取4
4读取OK
1读取OK

6:阻塞队列

6.1:队列

d1cbdfc5fd804b1c95ab9d6a23011c99.png

队列(FIFO)先进先出。

写入:如果队列满了,就必须阻塞等待。

读取:如果队列是空的,必须阻塞,等待生产,从而读取消息。

f72419cd0cc04193bd83be46620c7320.png

dbbf90fb838b4f08b77a24621c25217d.png

如下图所示:

d912ea9e551f43d3b4ea35a057a6cb3b.png

6.2 :四组API

6.2.1:抛出异常

添加:add()

移除:remove()

判断队首:element()

public class FIFO {
   public static void main(String[] args) {
      say();
   }
   public static void say(){
      ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
      System.out.println(blockingQueue.add("a"));
      System.out.println(blockingQueue.add("b"));
      System.out.println(blockingQueue.add("c"));
      //Exception in thread "main" java.lang.IllegalStateException: Queue full
      //System.out.println(blockingQueue.add("d"));
      System.out.println(blockingQueue.remove());
      System.out.println(blockingQueue.remove());
      System.out.println(blockingQueue.remove());
      //Exception in thread "main" java.util.NoSuchElementException
      //System.out.println(blockingQueue.remove());
   }
}

6.2.2:有返回值,不会抛出异常

添加:offer()

移除:poll()

判断队首:peek()

public class FIFO {
   public static void main(String[] args) {
      say2();
   }
   public static void say2(){
      ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
      //存
      System.out.println(blockingQueue.offer("a"));
      System.out.println(blockingQueue.offer("b"));
      System.out.println(blockingQueue.offer("c"));
      System.out.println(blockingQueue.offer("d"));
      //移除
      System.out.println(blockingQueue.poll());
      System.out.println(blockingQueue.poll());
      System.out.println(blockingQueue.poll());
      System.out.println(blockingQueue.poll());
   }
}
打印:
true
true
true
false
a
b
c
null

6.2.3:阻塞等待

取:put()

移除:take()

public class FIFO {
   public static void main(String[] args) throws InterruptedException {
      say3();
   }
   public static void say3() throws InterruptedException {
      //队列大小
      ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
      blockingQueue.put("a");
      blockingQueue.put("b");
      blockingQueue.put("c");
      //队列没有位置了,第四个一直阻塞
      //blockingQueue.put("d");
      System.out.println(blockingQueue.take());
      System.out.println(blockingQueue.take());
      System.out.println(blockingQueue.take());
      // 队列中没有元素了,第四个一直阻塞
      //System.out.println(blockingQueue.take());
   }
}

6.2.4:超时等待

public class FIFO {
   public static void main(String[] args) throws InterruptedException {
      say4();
   }
   public static void say4() throws InterruptedException {
      ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
      System.out.println(blockingQueue.offer("a"));
      System.out.println(blockingQueue.offer("b"));
      System.out.println(blockingQueue.offer("c"));
      System.out.println(blockingQueue.offer("d",2, TimeUnit.SECONDS));
      System.out.println(blockingQueue.poll());
      System.out.println(blockingQueue.poll());
      System.out.println(blockingQueue.poll());
      System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
   }
}

6.2.5 总结

方法摘要
ArrayBlockingQueue(int capacity) 创建具有给定(固定)容量和默认访问策略的 ArrayBlockingQueue
boolean add(E e) 在插入此队列的尾部,如果有可能立即这样做不超过该队列的容量,返回指定的元素 true成功时与抛出 IllegalStateException如果此队列已满。
boolean remove(Object o) 从该队列中删除指定元素的单个实例(如果存在)。
boolean offer(E e) 如果可以在不超过队列容量的情况下立即将其指定的元素插入该队列的尾部,则在成功时 false如果该队列已满,则返回 true
boolean offer(E e, long timeout, TimeUnit unit) 在该队列的尾部插入指定的元素,等待指定的等待时间,以使空间在队列已满时变为可用
E poll() 检索并删除此队列的头,如果此队列为空,则返回 null 。
E poll(long timeout, TimeUnit unit) 检索并删除此队列的头,等待指定的等待时间(如有必要)使元素变为可用
void put(E e) 在该队列的尾部插入指定的元素,如果队列已满,则等待空间变为可用
E take() 检索并删除此队列的头,如有必要,等待元素可用
E peek() 检索但不删除此队列的头,如果此队列为空,则返回 null

6.3: SynchronousQueue(同步队列)

public class 同步队列 {
   public static void main(String[] args) {
      BlockingQueue<String> queue = new SynchronousQueue<>();
      new Thread(()->{
         try {
            System.out.println(Thread.currentThread().getName()+"执行了a操作");
            queue.put("a");
            System.out.println(Thread.currentThread().getName()+"执行了b操作");
            queue.put("b");
            System.out.println(Thread.currentThread().getName()+"执行了c操作");
            queue.put("c");
         } catch (InterruptedException e) {
            throw new RuntimeException(e);
         }
      },"A").start();
      new Thread(()->{
         try {
            TimeUnit.SECONDS.sleep(3);
            System.out.println(Thread.currentThread().getName()+"读取了====a操作");
            queue.take();
            TimeUnit.SECONDS.sleep(3);
            System.out.println(Thread.currentThread().getName()+"读取了====b操作");
            queue.take();
            TimeUnit.SECONDS.sleep(3);
            System.out.println(Thread.currentThread().getName()+"读取了====c操作");
            queue.take();
         } catch (InterruptedException e) {
            throw new RuntimeException(e);
         }
      },"B").start();
   }
}
打印结果:
A执行了a操作
B读取了====a操作
A执行了b操作
B读取了====b操作
A执行了c操作
B读取了====c操作

7:线程池

三大方法,7大参数,4种拒绝策略

ac9ba5c1eed04ac3b298762cd378aadb.png

7.1 线程池的好处

1. 降低资源的消耗

2. 提高响应的速度

3. 方便管理

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

7.1.1  3大方法

public class pool {
   public static void main(String[] args) {
      ExecutorService threadPool = Executors.newCachedThreadPool();// 缓存线程池(遇强则强)
      ExecutorService threadPool2 = Executors.newFixedThreadPool(6);//固定线程池大小
      ExecutorService threadPool3 = Executors.newSingleThreadExecutor();//单一线程
      try {
         for (int i = 0; i < 100; i++) {
            threadPool.execute(()->{
               System.out.println(Thread.currentThread().getName());
            });
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      } finally {
         threadPool.shutdown();
      }
   }
}

7.1.2  7大参数

ThreadPoolExecutor

构造方法 :

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

创建一个新的 ThreadPoolExecutor与给定的初始参数。

参数
corePoolSize - (核心线程数)即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut
maximumPoolSize - 池中允许的最大线程数
keepAliveTime - 当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间
unit - keepAliveTime参数的时间单位
workQueue - 用于在执行任务之前使用的队列。 这个队列将仅保存execute方法提交的Runnable任务。(阻塞队列
threadFactory - 执行程序创建新线程时使用的工厂(线程工厂
handler - 执行被阻止时使用的处理程序,因为达到线程限制和队列容量(拒绝策略

91affb0345b6434699c74f080dd53256.png

 7.1.2  四种拒绝策略

RejectedExecutionHandler
ThreadPoolExecutor.AbortPolicy 被拒绝的任务的处理程序,抛出一个 RejectedExecutionException 。(银行满了,还有人进来,不处理这个人的,抛出异常
ThreadPoolExecutor.CallerRunsPolicy 一个被拒绝的任务的处理程序,直接在 execute方法的调用线程中运行被拒绝的任务,除非执行程序已被关闭,否则这个任务被丢弃。(哪来回哪去
ThreadPoolExecutor.DiscardOldestPolicy 被拒绝的任务的处理程序,丢弃最旧的未处理请求,然后重试 execute ,除非执行程序被关闭,在这种情况下,任务被丢弃。
ThreadPoolExecutor.DiscardPolicy 被拒绝的任务的处理程序静默地丢弃被拒绝的任务

7.1.2.1  代码示例:

/**
 * new ThreadPoolExecutor.AbortPolicy()  银行满了,还有人进来,不处理这个人的,抛出异常)
 * new ThreadPoolExecutor.CallerRunsPolicy()      哪来的回哪去
 * new ThreadPoolExecutor.DiscardOldestPolicy()   队列满了不会抛出异常
 * new ThreadPoolExecutor.DiscardOldestPolicy()   队列满了。尝试去跟第一个线程竞争。如果没竞争过,还是回丢弃任务
 *                                                不会抛出异常
 */
public class newPool {
   public static void main(String[] args) {
      ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2,5,3,
              TimeUnit.SECONDS,
              new LinkedBlockingQueue<>(3),
              Executors.defaultThreadFactory(),
              new ThreadPoolExecutor.DiscardOldestPolicy());
      try {
         //最大承载:Queue + max = 5 + 3 = 8
         //超过RejectedExecution
         for (int i = 0; i < 9  ; i++) {
            poolExecutor.execute(()->{
               System.out.println(Thread.currentThread().getName());
            });
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      } finally {
         poolExecutor.shutdown();
      }
   }
}

8:了解CPU密集型和IO密集型(池的大小)

package main;
import java.util.concurrent.*;
public class ExecutorServiceDemo {
    public static void main(String[] args) {
        /*
        * 最大线程到底该如何定义
        * 1.CPU密集型,几核,最大线程就是几,可以保持CPU的效率最高
        * 2.IO密集型  大于你程序中十分耗IO的线程
        * */
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),
                3,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
        try {
            //最大承载:Queue + max
            //超过RejectedExecution
            for (int i = 0; i < 9; i++) {
                //使用线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" : ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}
目录
相关文章
|
2天前
|
安全 调度 Swift
【Swift开发专栏】Swift中的多线程与并发编程
【4月更文挑战第30天】本文探讨Swift中的多线程与并发编程,分为三个部分:基本概念、并发编程模型和最佳实践。介绍了线程、进程、并发与并行、同步与异步的区别。Swift的并发模型包括GCD、OperationQueue及新引入的结构体Task和Actor。编写高效并发代码需注意任务粒度、避免死锁、使用线程安全集合等。Swift 5.5的并发模型简化了异步编程。理解并掌握这些知识能帮助开发者编写高效、安全的并发代码。
|
2天前
|
调度 Python
Python并发编程模型:面试中的重点考察点
【4月更文挑战第14天】Python并发编程包括多线程、多进程和协程,常用于提高系统响应和资源利用率。多线程简单但受限于GIL;多进程可规避GIL,但通信开销大;协程适合IO密集型任务,学习成本较高。面试常见问题涉及并发并行概念、GIL影响、进程间通信同步及协程的异步IO理解。掌握并发模型的选择与应用,能有效提升面试表现。
27 0
|
2天前
|
Java Go 调度
Go语言并发编程原理与实践:面试经验与必备知识点解析
【4月更文挑战第12天】本文分享了Go语言并发编程在面试中的重要性,包括必备知识点和面试经验。核心知识点涵盖Goroutines、Channels、Select、Mutex、Sync包、Context和错误处理。面试策略强调结构化回答、代码示例及实战经历。同时,解析了Goroutine与线程的区别、Channel实现生产者消费者模式、避免死锁的方法以及Context包的作用和应用场景。通过理论与实践的结合,助你成功应对Go并发编程面试。
27 3
|
2天前
|
C++
C++语言多线程学习应用案例
使用C++ `std::thread`和`std::mutex`实现多线程同步。示例创建两个线程`t1`、`t2`,共享资源`shared_resource`,每个线程调用`increase`函数递增资源值。互斥锁确保在任何时候只有一个线程访问资源,防止数据竞争。最后输出资源总值。
10 1
|
2天前
|
UED 开发者 Python
Python中的并发编程技术探究
本文将深入探讨Python中的并发编程技术,介绍多线程、多进程、协程等概念及其在实际项目中的运用,帮助读者更好地理解并发编程的重要性和应用场景。
|
2天前
|
设计模式 Java Go
Go语言高级面向对象编程技巧与实战案例
【2月更文挑战第10天】本文将深入探讨Go语言中的高级面向对象编程技巧,并通过实战案例展示如何应用这些技巧解决实际问题。我们将了解如何使用设计模式、测试与调试面向对象程序、性能优化与内存管理等高级话题,以提升Go语言编程的水平和代码质量。
|
2天前
|
程序员 调度 开发者
深入浅出Python协程:提升代码效率的艺术
在当今快速发展的软件开发领域,提高代码执行效率成为了每个开发者的追求。本文将带您深入理解Python协程(Coroutine)的概念、工作原理及其在异步编程中的应用,通过对比传统的同步编程方式,展示协程如何优雅地处理I/O密集型任务,从而显著提升程序的运行效率。文章结合实例讲解如何在Python中使用协程,以及如何通过asyncio库来实现高效的异步编程模式,旨在为读者提供一个清晰、易于理解的协程学习路径。
43 1
|
9月前
|
SQL Web App开发 安全
UC并发编程学习笔记(简单易懂)4
UC并发编程学习笔记(简单易懂)
41 0
|
9月前
|
算法 Unix 程序员
C++简介 C语言编程原理
C++简介 C语言编程原理
|
11月前
|
安全 Go 调度
Go语言进阶之并发编程 | 青训营笔记
Go语言进阶之并发编程 | 青训营笔记
63 0
Go语言进阶之并发编程 | 青训营笔记

热门文章

最新文章