Java多线程与并发编程

课程地址:
https://www.itlaoqi.com/chapter.html?sid=98&cid=1425
源码文档:
链接:https://pan.baidu.com/s/1WMvM3j6qhyjIeAT87kIcxg
提取码:5g56

Java多线程与并发编程

      • 1-并发背后的故事
          • 什么是并发
      • 2-你必须知道线程的概念
          • 程序、进程与线程
          • 并发和并行
          • 同步和异步
          • 临界区
          • 线程活跃度(死锁,饥饿,活锁)
          • 线程安全
      • 3-Java内存模型(JMM)
          • Java内存模型全称JMM(Java Memory Model)
      • 4-创建多线程-继承Thread
      • 5-创建多线程-实现Runnable接口
      • 6-创建多线程-实现Callable接口
      • 7-Synchronized线程同步机制
          • Synchronize的使用场景
      • 8-面试题-线程的五种状态
      • 9-死锁的产生
      • 10-重新认识线程安全ThreadSafe
      • 11-JAVA并发包与线程池
          • 什么是线程池
          • new Thread的弊端
          • ThreadPool - 线程池
          • 线程池的种类
      • 12-JUC之CountDownLatch倒计时锁
      • 13-JUC之Semaphore信号量
      • 14-JUC之CyclicBarrier循环屏障
          • CyclicBarrier的应用场景
      • 15-JUC之ReentrantLock重入锁
          • ReentrantLock与synchronized的区别
      • 15.1-JUC之Condition线程等待与唤醒
      • 16-JUC之Callable_Future
      • 17-JUC之同步容器
          • 请写出线程安全的类
          • 线程安全-并发容器
      • 18-JUC之Atomic与CAS算法(乐观锁)
          • 回顾原子性
          • Atomic包
          • Atomic的应用场景
      • 19-课程总结

1-并发背后的故事

什么是并发

并发就是指程序同时处理多个任务的能力。
并发编程的根源在于对多任务情况下对访问资源的有效控制

例如多人同时操作在线文档等
在这里插入图片描述

2-你必须知道线程的概念

程序、进程与线程

程序是静态的概念,windows下通常指exe文件。

进程是动态的概念,是程序在运行状态,进程说明程序在内存中的边界。
线程是进程内的一个”基本任务”,每个线程都有自己的功能,是CPU分配与调度的基本单位。

并发和并行

并行是基于多核cpu进行,多个线程同时调度任务
并发是单个cpu处理

在这里插入图片描述

同步和异步

同步,前面的事情不做完后面的事情干不了
异步,异步是指在程序执行过程中,不需要等待某个任务的完成,而是在发出该任务后继续执行后面的代码,等任务完成后再回来处理该任务的结果

在这里插入图片描述

临界区

临界区用来表示一种公共资源与共享数据,可以被多个线程使用。
同一时间只能有一个线程访问临界区(阻塞状态),其他资源必须等待。

举例:数据库在执行某条数据更新操作,数据库为了保证数据访问有效,所以会在更新前对这条数据开启一个锁,这个锁只能被其中某个用户访问,这个用户对这条数据更新的时候,其他用户对这条数据更新都得等着,直到更新的的用户更新完把锁释放掉,其他等待的用户才能够更新。

线程活跃度(死锁,饥饿,活锁)

死锁,大家对公共资源彼此争执,并且都不愿意释放
饥饿,线程一直获取不到资源
活锁,线程调度不够智能,资源没有被占用,线程还一直处于等待状态
三种情况都会形成系统阻塞
在这里插入图片描述

线程安全

在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
线程安全和不安全,取决于在多线程的情况下,在对比单线程所造成的结果是否相同,如果是相同则是线程安全的,结果不一致则可以定义为线程不安全

3-Java内存模型(JMM)

Java内存模型全称JMM(Java Memory Model)

内存主要有堆和栈组成
在这里插入图片描述
下面来一段demo代码详细讲解堆栈的作用,以及流程

public class Employee {private String name;private Integer age;private Department department;public Employee(){}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Department getDepartment() {return department;}public void setDepartment(Department department) {this.department = department;}public void sayJoke(String content){System.out.println(this.getName() + "说" + content);}public static void main(String[] args) {Employee emp = new Employee();emp.setName("老齐");emp.setAge(13);Department department = new Department();department.setDname("小卖部");emp.setDepartment(department);emp.sayJoke("一言不合就开车");}
}class Department{private String dname;public String getDname() {return dname;}public void setDname(String dname) {this.dname = dname;}
}

在这里插入图片描述
1.运行方法前进行类的加载,加载到employee和department这两个类,把这两个类的结构,成员变量和成员方法加载到方法区(方法区静态还会存储静态的方法和变量
2.启动main方法,创建一个线程,会开辟一个栈空间,压入mian方法的栈(栈帧:每执行一个方法就会有一个对应的栈帧
3.emp = new Empolyee(); 创建对象,存储在堆空间,将栈地址指向开辟的堆空间(也可以叫做引用
4.emp.setName(“老齐”);先在方法区创建字符串"老齐",然后将字符串引用到name属性上,setName()方法的栈帧执行完,就会出栈
5.emp.setAge(13);数字按值引用,直接赋值,不需要在静态区开辟新空间
6.dept = new Department();同上3
7.dept.setDname(“小卖铺”);同上4
8.emp.setDepartment(dept);Employee对象的department属性直接指向Department对象的地址(也可以叫做地址被属性所引用)
9.emp.sayJoke(“一言不合就开车”)
10.方法执行完成,站内栈帧全部弹出,线程销毁

4-创建多线程-继承Thread

/*** 使用集成Thread的方式实现多线程*/
public class Match1 {public static void main(String[] args) {Runner liuxiang = new Runner();//创建一个新的线程liuxiang.setName("刘翔");//设置线程名称Runner laoqi = new Runner();laoqi.setName("老齐");Runner op = new Runner();op.setName("路飞");liuxiang.start();//启动线程laoqi.start();op.start();}
}
class Runner extends Thread{@Overridepublic void run() {Integer speed = new Random().nextInt(100);for(int i = 1 ; i <= 100 ; i++){try {Thread.sleep(1000); //当前线程休眠1秒}catch (Exception e){e.printStackTrace();}//this.getName()打印当前线程的名字System.out.println(this.getName() + "已前进" + (i * speed) + "米(" + speed + "米/秒)");}}
}

5-创建多线程-实现Runnable接口

public class Match2 {public static void main(String[] args) {Runner2 liuxiang = new Runner2();Thread thread1 = new Thread(liuxiang);thread1.setName("刘翔");Thread laoqi = new Thread(new Runner2());laoqi.setName("老齐");Thread op = new Thread(new Runner2());op.setName("路飞");thread1.start();laoqi.start();op.start();}
}class Runner2 implements Runnable {@Overridepublic void run() {Integer speed = new Random().nextInt(100);for(int i = 1 ; i <= 100 ; i++){try {Thread.sleep(1000); //当前线程休眠1秒}catch (Exception e){e.printStackTrace();}//Thread.currentThread()用于获取当前执行的线程对象//在Runnable中是无法使用this获取到当前线程对象的System.out.println(Thread.currentThread().getName() + "已前进" + (i * speed) + "米(" + speed + "米/秒)");}}
}

6-创建多线程-实现Callable接口

并发工具包-Concurrent

JDK1.5以后为我们专门提供了一个并发工具包java.util.concurrent。
java.util.concurrent 包含许多线程安全、测试良好、高性能的并发构建块。创建 concurrent 的目的就是要实现 Collection 框架对数据结构所执行的并发操作。通过提供一组可靠的、高性能并发构建块,开发人员可以提高并发类的线程安全、可伸缩性、性能、可读性和可靠性

public class Match3 {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建一个线程池。里面天生有3个“空”线程。Executors是调度器,对线程池进行管理ExecutorService executorService =  Executors.newFixedThreadPool(3);Runner3 liuxiang = new Runner3();//实例化Callable对象liuxiang.setName("刘翔");Runner3 laoqi = new Runner3();laoqi.setName("老齐");Runner3 op = new Runner3();op.setName("路飞");//将这个对象扔到线程池中,线程池自动分配一个线程来运行liuxiang这个对象的call方法//Future用于接受线程内部call方法的返回值Future<Integer> result1 =  executorService.submit(liuxiang);Future<Integer> result2 =  executorService.submit(laoqi);Future<Integer> result3 =  executorService.submit(op);try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}executorService.shutdown();//关闭线程池释放所有资源System.out.println("刘翔累计跑了" + result1.get() + "米" );System.out.println("老齐累计跑了" + result2.get() + "米" );System.out.println("路飞累计跑了" + result3.get() + "米" );}
}
class Runner3 implements Callable<Integer>{private String name ;public void setName(String name){this.name = name;}//实现Callable接口可以允许我们的线程返回值或抛出异常@Overridepublic Integer call() throws Exception {Integer speed = new Random().nextInt(100);Integer distince = 0; //总共奔跑的距离for(int i = 1 ; i <= 100 ; i++){Thread.sleep(10);distince = i * speed;System.out.println(this.name + "已前进" + distince + "米(" + speed + "米/秒)");}return distince;}
}

三种线程创建方式的对比
在这里插入图片描述

7-Synchronized线程同步机制

同步代码样例

public class SyncSample {public static void main(String[] args) {Couplet c = new Couplet();for(int i = 0 ; i < 10000 ; i++){new Thread(){public void run(){int r = new Random().nextInt(2);if(r % 2 == 0){Couplet.first();}else{Couplet.second();}}}.start();}}
}
class Couplet{Object lock = new Object(); //锁对象public synchronized static void first(){
//        synchronized (lock) { //同步代码块,在同一时间只允许有一个线程执行访问这个方法System.out.printf("琴");System.out.printf("瑟");System.out.printf("琵");System.out.printf("琶");System.out.println();
//        }}public static void second(){synchronized (Couplet.class) { //因为两个同步代码指向了同一把锁lock,所以在同一个时间内只允许有一个代码块执行,其他等待System.out.printf("魑");System.out.printf("魅");System.out.printf("魍");System.out.printf("魉");System.out.println();}}
}

现实中同步的列子
在这里插入图片描述

代码中同步的列子
synchronized(同步锁)关键字的作用就是利用一个特定的对象设置一个锁lock(绣球),在多线程(游客)并发访问的时候,同时只允许一个线程(游客)可以获得这个锁,执行特定的代码(迎娶新娘)。执行后释放锁,继续由其他线程争抢。

Synchronize的使用场景

Synchronize可以使用在以下三种场景,对应不同锁对象:
synchronized代码块 - 任意对象即可
synchronized方法 - this当前对象
synchronized静态方法 - 该类的字节码对象

8-面试题-线程的五种状态

新建,当线程被new出来时,处于新建状态
就绪,等待状态,等待cpu分配资源(时间片),当分配到资源,就会自动执行run()
运行,程序运行状态
阻塞,当锁被解除或者休眠时间到了,线程就处于就绪状态,等待cpu分配资源
》I/O(下载一个很大数据的文件需要很长时间),
》sleep()(线程休眠),
》lock锁(比如synchronized关键字,其中就有等待的机制),
》yield()(主动把当前cpu的时间让出去,让给优先级更高的线程来去执行),
死亡,当程序所有都运行完,当前线程就会被jvm自动销毁,进入死亡状态,被垃圾回收
在这里插入图片描述

9-死锁的产生

举例两个线程运用不同的顺序操作AB文件:

public class DeadLock {private static String fileA = "A文件";private static String fileB = "B文件";public static void main(String[] args) {new Thread(){ //线程1public void run(){while(true) {synchronized (fileA) {//打开文件A,线程独占System.out.println(this.getName() + ":文件A写入");synchronized (fileB) {System.out.println(this.getName() + ":文件B写入");}System.out.println(this.getName() + ":所有文件保存");}}}}.start();new Thread(){ //线程2public void run(){while(true) {synchronized (fileB) {//打开文件A,线程独占System.out.println(this.getName() + ":文件B写入");synchronized (fileA) {System.out.println(this.getName() + ":文件A写入");}System.out.println(this.getName() + ":所有文件保存");}}}}.start();}
}

10-重新认识线程安全ThreadSafe

线程安全,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

举例多线程情况下的计数器

public class DownloadsSample {public static int users = 100;//同时模拟的并发访问用户数量public static int downTotal = 50000; //用户下载的真实总数public static int count = 0 ;//计数器public static void main(String[] args) {//调度器,JDK1.5后提供的concurrent包对于并发的支持ExecutorService executorService  = Executors.newCachedThreadPool();//信号量,用于模拟并发的人数final Semaphore semaphore = new Semaphore(users);for(int i = 0 ; i < downTotal ; i++){executorService.execute(()->{//通过多线程模拟N个用户并发访问并下载try {semaphore.acquire();add();semaphore.release();} catch (Exception e) {e.printStackTrace();}});}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}executorService.shutdown();//关闭调度服务System.out.println("下载总数:" + count);}//线程不安全public static void add(){count++;}/*线程安全public synchronized static void add(){count++;}*/}

在这里插入图片描述
在这里插入图片描述

11-JAVA并发包与线程池

什么是线程池

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

new Thread的弊端

new Thread()新建对象,性能差
线程缺乏统一管理,可能无限制的新建线程,相互竞争,严重时会占用过多系统资源导致死机或OOM

ThreadPool - 线程池

》 重用存在的线程,减少对象对象、消亡的开销
》 线程总数可控,提高资源的利用率
》 避免过多资源竞争,避免阻塞
》提供额外功能,定时执行、定期执行、监控等。

线程池的种类

在java.util.concurrent中,提供了工具类Executors(调度器)对象来创建线程池,可创建的线程池有四种:

  1. CachedThreadPool - 可缓存线程池
  2. FixedThreadPool - 定长线程池
  3. SingleThreadExecutor - 单线程池
  4. ScheduledThreadPool - 调度线程池

CachedThreadPool

public class ThreadPoolSample1 {public static void main(String[] args) {//调度器对象//ExecutorService用于管理线程池ExecutorService threadPool = Executors.newCachedThreadPool();//创建一个可缓存线程池//可缓存线程池的特点是,无限大,如果线程池中没有可用的线程则创建,有空闲线程则利用起来for(int i = 1 ; i <= 1000 ; i++) {final  int index = i;threadPool.execute(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + ":" + index);}});}try {Thread.sleep(1000); //跟线程足够的运行时间} catch (InterruptedException e) {e.printStackTrace();}//shutdown() 代表关闭线程池(等待所有线程完成)//shutdownNow() 代表立即终止线程池的运行,不等待线程,不推荐使用threadPool.shutdown();}
}

FixedThreadPool

public class ThreadPoolSample2 {public static void main(String[] args) {//调度器对象//ExecutorService用于管理线程池ExecutorService threadPool = Executors.newFixedThreadPool(10);//创建一个可创建一个定长线程池//定长线程池的特点是固定线程总数,空间线程用于执行任务,如果线程都在使用后续任务则处于等待状态,在线程池中的线程//如果任务处于等待的状态,备选的等待算法默认为FIFO(先进先出) LIFO(后进先出)//执行任务后再执行后续的任务。for(int i = 1 ; i <= 1000 ; i++) {final  int index = i;threadPool.execute(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + ":" + index);}});}try {Thread.sleep(1000); //跟线程足够的运行时间} catch (InterruptedException e) {e.printStackTrace();}//shutdown() 代表关闭线程池(等待所有线程完成)//shutdownNow() 代表立即终止线程池的运行,不等待线程,不推荐使用threadPool.shutdown();}
}

SingleThreadExecutor

public class ThreadPoolSample3 {public static void main(String[] args) {//调度器对象//ExecutorService用于管理线程池ExecutorService threadPool = Executors.newSingleThreadExecutor();//单线程线程池for(int i = 1 ; i <= 1000 ; i++) {final  int index = i;threadPool.execute(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + ":" + index);}});}try {Thread.sleep(1000); //跟线程足够的运行时间} catch (InterruptedException e) {e.printStackTrace();}//shutdown() 代表关闭线程池(等待所有线程完成)//shutdownNow() 代表立即终止线程池的运行,不等待线程,不推荐使用threadPool.shutdown();}
}

ScheduledThreadPool

public class ThreadPoolSample4 {public static void main(String[] args) {ScheduledExecutorService scheduledThreadPool =  Executors.newScheduledThreadPool(5);//可调度线程池/*//延迟三秒执行一次Run方法scheduledThreadPool.schedule(new Runnable() {@Overridepublic void run() {System.out.println("延迟3秒执行");}} , 3 , TimeUnit.SECONDS);*///Timer , 项目实际开发中scheduledThreadPool与Timer都不会用到,应为有成熟的调度框架Quartz,或者Spring自带调度,//程序的调度框架支持一种表达式叫做Cron表达式,有兴趣的童鞋可以了解一下。scheduledThreadPool.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println(new Date() + "延迟1秒执行,每三秒执行一次");}}, 1, 3, TimeUnit.SECONDS);}
}

12-JUC之CountDownLatch倒计时锁

CountDownLatch倒计时锁特别适合”总-分任务”,例如多线程计算后的数据汇总
CountDownLatch类位于java.util.concurrent(J.U.C)包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他3个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

举例:我们需要计算三个子线程完成计算的结果汇总,设置countDownLatch等于3,子线程执行完一次countdownLatch就会减一,当三个线程都执行完成,countDownLatch等于0时,就会执行汇总任务
在这里插入图片描述
案例:多线程的情况下计算10000

public class CountDownSample {private static int count = 0;public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(100);CountDownLatch cdl = new CountDownLatch(10000); //CDL总数和操作数保持一致for(int i = 1 ; i <= 10000 ; i++) {final int index = i;threadPool.execute(new Runnable() {@Overridepublic void run() {synchronized (CountDownSample.class) {try {count = count + index;//计数器减一}catch(Exception e){e.printStackTrace();}finally {cdl.countDown();}}}});}
/*        try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}*/try {cdl.await(); //堵塞当前线程,直到cdl=0的时候再继续往下走//为了避免程序一致挂起,我们可以设置一个timeout时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println(count);threadPool.shutdown();}
}

13-JUC之Semaphore信号量

Semaphore信号量经常用于限制获取某种资源的线程数量,例如限制游戏服务器在线人数,降低服务器运行压力,避免系统扛不住导致宕机
在这里插入图片描述
案例:假设服务器只能容纳5人游戏,20人应该怎么处理(当5个信号量被占用时,其他的线程必须等到5人中有人释放信号量,才能执行)

public class SemaphoreSample1 {public static void main(String[] args) {ExecutorService threadPool = Executors.newCachedThreadPool();Semaphore semaphore = new Semaphore(5);//定义5个信号量,也就是说服务器只允许5个人在里面玩for(int i = 1 ; i <= 20 ; i++) {final int index = i;threadPool.execute(new Runnable() {@Overridepublic void run() {try {semaphore.acquire();//获取一个信号量,“占用一个跑到”play();semaphore.release();//执行完成后释放这个信号量,“从跑道出去”} catch (InterruptedException e) {e.printStackTrace();}}});}threadPool.shutdown();}public static void play(){try {System.out.println(new Date() + " " + Thread.currentThread().getName() + ":获得紫禁之巅服务器进入资格");Thread.sleep(2000);System.out.println(new Date() + " " + Thread.currentThread().getName() + ":退出服务器");Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}
}

14-JUC之CyclicBarrier循环屏障

可以让所有的线程同时执行,线程执行到barrier时会被拦住,知道所有的线程都准备就绪,然后同时执行
在这里插入图片描述

public class CyclicBarrierSample {private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5);public static void main(String[] args) {ExecutorService executorService = Executors.newCachedThreadPool();for(int i = 1 ; i<=20 ; i++) {final int index = i;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}executorService.execute(new Runnable() {@Overridepublic void run() {go();}});}executorService.shutdown();}private static void go(){System.out.println(Thread.currentThread().getName() + ":准备就绪" );try {cyclicBarrier.await();//设置屏障点,当累计5个线程都准备好后,才运行后面的代码System.out.println(Thread.currentThread().getName() + ":开始运行");} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}
}
CyclicBarrier的应用场景

cpu性能测试
双11秒杀活动
抢票软件
在这里插入图片描述

15-JUC之ReentrantLock重入锁

什么是重入锁

重入锁是指任意线程在获取到锁之后,再次获取该锁而不会被该锁所阻塞
ReentrantLock设计的目标是用来替代synchronized关键字

ReentrantLock与synchronized的区别

在这里插入图片描述

public class ReentrantLockSample {public static int users = 100;//同时模拟的并发访问用户数量public static int downTotal = 50000; //用户下载的真实总数public static int count = 0 ;//计数器private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {//调度器,JDK1.5后提供的concurrent包对于并发的支持ExecutorService executorService  = Executors.newCachedThreadPool();//信号量,用于模拟并发的人数final Semaphore semaphore = new Semaphore(users);for(int i = 0 ; i < downTotal ; i++){executorService.execute(()->{//通过多线程模拟N个用户并发访问并下载try {semaphore.acquire();add();semaphore.release();} catch (Exception e) {e.printStackTrace();}});}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}executorService.shutdown();//关闭调度服务System.out.println("下载总数:" + count);}//线程不安全public static void add(){lock.lock();//上锁try {count++;}finally {lock.unlock(); //解锁,一定要放在finally里面否则会出现死锁}}}

15.1-JUC之Condition线程等待与唤醒

condition条件唤醒

我们在并行程序中,避免不了某些线程要预先规定好的顺序执行,例如:先新增再修改,先买后卖,>先进后出…,对于这类场景,使用JUC的Condition对象再合适不过了。
JUC中提供了Condition对象,用于让指定线程等待与唤醒,按预期顺序执行。它必须和ReentrantLock重入锁配合使用。
Condition用于替代wait()/notify()方法

notify只能随机唤醒等待的线程,而Condition可以唤醒指定的线程,这有利于更好
的控制并发程序。

Condition核心方法

await() - 阻塞当前线程,直到singal唤醒
signal() - 唤醒被await的线程,从中断处继续执行
signalAll() - 唤醒所有被await()阻塞的线程

16-JUC之Callable_Future

Callable和Runnable一样代表着任务,区别在于Callable有返回值并且可以抛出异常。
Future 是一个接口。它用于表示异步计算的结果。提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。

案例:打印出10000以内的质数

public class FutureSample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(10);for(int i = 2 ; i <= 10000 ; i++){Computor c = new Computor();c.setNum(i);//Future是对用于计算的线程进行监听,因为计算是在其他线程中执行的,所以这个返回结果的过程是异步的Future<Boolean> result = executorService.submit(c);//将c对象提交给线程池,如有空闲线程立即执行里面的call方法try {Boolean r = result.get(); //用于获取返回值,如果线程内部的call没有执行完成,则进入等待状态,直到计算完成if(r == true){System.out.println(c.getNum());}} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}executorService.shutdown();}
}
class Computor implements Callable<Boolean>{private Integer num;public Integer getNum() {return num;}public void setNum(Integer num) {this.num = num;}@Overridepublic Boolean call() throws Exception {boolean isprime = true;for(int i = 2 ; i < num ; i++) {if (num % i == 0) {isprime = false;break;}}return isprime;}
}

17-JUC之同步容器

请写出线程安全的类

Vector是线程安全的,ArrayList、LinkedList是线程不安全的
Properties是线程安全的,HashSet、TreeSet是不安全的
StringBuffer是线程安全的,StringBuilder是线程不安全的
HashTable是线程安全的,HashMap是线程不安全的

线程安全-并发容器

ArrayList -> CopyOnWriteArrayList - 写复制列表
HashSet -> CopyOnWriteArraySet - 写复制集合
HashMap -> ConcurrentHashMap - 分段锁映射

CopyOnWriteArrayList案例

public class CopyOnWriteArrayListSample {public static void main(String[] args) {//写复制列表List<Integer> list = new CopyOnWriteArrayList<>();for(int i = 0 ; i < 1000 ; i++){list.add(i);}Iterator<Integer> itr = list.iterator();while (itr.hasNext()) {Integer i = itr.next();list.remove(i);}System.out.println(list);}
}

List和Set底层数据结构都是数组,只不过一个是有序一个是无序,在多线程的情况下,进行add()和remove()时,会报ConcurrentModificationException并发修改异常,推荐使用CopyOnWriteArrayList和CopyOnWriteArraySet,原理就是在新增一条数据的时候,新创建一个副本,对副本进行操作,然后讲原来的指针指向新的副本地址,源码
在这里插入图片描述

public boolean add(E e) {// 重入锁final ReentrantLock lock = this.lock;// 加锁lock.lock();try {Object[] elements = getArray();int len = elements.length;// 复制原来的list,并且长度加一Object[] newElements = Arrays.copyOf(elements, len + 1);newElements[len] = e;// 引用新的副本setArray(newElements);return true;} finally {// 释放锁lock.unlock();}}

hashMap在多线程的情况下也是线程不安全的,线程安全的有hashTable,但是为什么实际不适用hashTable,而是用ConcurrentHashMap呢

  • hashTable底层时使用synchronized关键字实现,是多个线程操作同一个map,效率低
  • ConcurrentHashMap使用的是分段锁,将map以2*n次方分为多端长度,每个分段之间可以同时进行操作,但是单个分段还是单个操作,对于hashTable效率高
    在这里插入图片描述

18-JUC之Atomic与CAS算法(乐观锁)

回顾原子性

原子性:是指一个操作或多个操作要么全部执行,且执行的过程不会被任何因素打断,要么就都不执行。

Atomic包

Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。

Atomic常用类
– AtomicInteger
– AtomicIntegerArray
– AtomicBoolean
– AtomicLong
– AtomicLongArray

将Atomic之前,先说下乐观锁和悲观锁的概念,其实意如其名
在这里插入图片描述

Atomic的应用场景

虽然基于CAS的线程安全机制很好很高效,但要说的是,并非所有线程安全都可以用这样的方法来实现,这只适合一些粒度比较小型,如计数器这样的需求用起来才有效,否则也不会有锁的存在了。

19-课程总结

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/108512.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【RISC-V】RISC-V寄存器简介

一、通用寄存器 32位RISC-V体系结构提供32个32位的整型通用寄存器寄存器别名全称说明X0zero零寄存器可做源寄存器(rs)或目标寄存器(rd)X1ra链接寄存器保存函数返回地址X2sp栈指针寄存器指向栈的地址X3gp全局寄存器用于链接器松弛优化X4tp线程寄存器常用于在OS中保存指向进程控…

第十三课 宾语从句

文章目录 前言一、宾语从句1、主语及物动词宾语从句2、主语双宾动词间接宾语直接宾语3、主语特定及物动词宾语从句&#xff08;作宾语&#xff09;宾补4、主语be某些形容词宾语从句5、动词不定式后面的宾语从句6、动名词后面的宾语从句7、介词后面的宾语从句9、间接引语 前言 一…

[Go版]算法通关村第十四关白银——堆高效解决的经典问题(在数组找第K大的元素、堆排序、合并K个排序链表)

目录 题目&#xff1a;在数组中找第K大的元素解法1&#xff1a;维护长度为k的最小堆&#xff0c;遍历n-k个元素&#xff0c;逐一和堆顶值对比后&#xff0c;和堆顶交换&#xff0c;最后返回堆顶复杂度&#xff1a;时间复杂度 O ( k ( n − k ) l o g k ) O(k(n-k)logk) O(k(n−…

Redis各类数据结构应用场景总结

Redis各类数据结构应用场景总结 引言String应用场景 List应用场景 Hash应用场景 Set应用场景 ZSet应用场景 小结 引言 实际面试过程中更多看重的是对Redis相关数据结构的活学活用&#xff0c;同时也可能会引申出Redis相关底层数据结构原理的实现&#xff0c;笔者最近面试过程中…

高效公文校对与文字处理:走进自然语言技术的新时代

在数字化时代的浪潮中&#xff0c;无论是政府材料、新闻稿、还是发言稿&#xff0c;高质量的文字内容成为了信息传递的核心。为了确保内容的专业性和准确性&#xff0c;公文校对和文字处理技术的进步成为了不可或缺的关键。本文将深入探讨自然语言处理技术如何为公文校对和文字…

DMK5框选变量之后不显示其他位置的此变量高亮

使用软件MDK5.3.8版本 如下在2的位置选择之后&#xff0c;其他同样的变量没有高亮&#xff0c;因为1的原因折叠了&#xff1b; 展开折叠之后就可以了

如何使用CSS实现一个水平居中和垂直居中的布局?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 水平居中布局⭐ 垂直居中布局⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣…

使用代理突破浏览器IP限制

一、实验目的: 主要时了解代理服务器的概念&#xff0c;同时如何突破浏览器IP限制 二、预备知识&#xff1a; 代理服务器英文全称是Proxy Server&#xff0c;其功能就是代理网络用户去取得网络信息。形象的说&#xff1a;它是网络信息的中转站&#xff0c;特别是它具有一个cac…

数据分析作业2

中国在 2020 年开展第七次全国人口普查&#xff0c;截止 2021 年 5 月 11 日普查结果公布&#xff0c;全国人口共1411778724人。单从数据表格看相关数据不够直观&#xff0c;需要进行数据可视化展示&#xff0c;方便查看数据结果。 任务一&#xff1a;链接 MySQL 数据库&#x…

Python爬虫框架之Selenium库入门:用Python实现网页自动化测试详解

概要 是否还在为网页测试而烦恼&#xff1f;是否还在为重复的点击、等待而劳累&#xff1f;试试强大的Selenium&#xff01;让你的网页自动化测试变得轻松有趣&#xff01; 一、Selenium库到底是什么&#xff1f; Selenium 是一个强大的自动化测试工具&#xff0c;它可以让你直…

前端学习记录~2023.8.10~JavaScript重难点实例精讲~第6章 Ajax

第 6 章 Ajax 前言6.1 Ajax的基本原理及执行过程6.1.1 XMLHttpRequest对象&#xff08;1&#xff09;XMLHttpRequest对象的函数&#xff08;2&#xff09;XMLHttpRequest对象的属性 6.1.2 XMLHttpRequest对象生命周期&#xff08;1&#xff09;创建XMLHttpRequest对象&#xff…

Scikit-Learn中的特征选择和特征提取详解

概要 机器学习在现代技术中扮演着越来越重要的角色。不论是在商业界还是科学领域&#xff0c;机器学习都被广泛地应用。在机器学习的过程中&#xff0c;我们需要从原始数据中提取出有用的特征&#xff0c;以便训练出好的模型。但是&#xff0c;如何选择最佳的特征是一个关键问…

RK3399平台开发系列讲解(存储篇)Linux 存储系统的 I/O 栈

平台内核版本安卓版本RK3399Linux4.4Android7.1🚀返回专栏总目录 文章目录 一、Linux 存储系统全景二、Linux 存储系统的缓存沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 Linux 存储系统的 I/O 原理。 一、Linux 存储系统全景 我们可以把 Linux 存储系…

opencv的haarcascade_frontalface_default.xml等文件

文章目录 GitHub下载在安装好的OpenCV文件夹下寻找opencv-python中获取 GitHub下载 下载地址&#xff1a;https://github.com/opencv/opencv/tree/master/data/haarcascades 在安装好的OpenCV文件夹下寻找 路径如下&#xff1a; 你安装的opencv路径\OpenCV\opencv\build\et…

基于飞腾芯片的设计与调试入门指导

一、啥是自主可控 国产CPU现在厂家细算起来其实有很多,现在华为、小米也在做自己的CPU,瑞芯微、全志等的SoC现在也是广泛应用。但是真正能叫做自主可控的CPU厂商,只有6家。那啥是自主可控?首先来不严谨的讲下现在数字芯片是怎么做的设计。FPGA大家都知道,可以通过Verilog…

Matlab 使用经验分享(常用函数介绍;矩阵常见计算)

Matlab 使用经验分享 大家好&#xff01;最近有很多朋友询问我关于 Matlab 的使用&#xff0c;于是我决定写一篇博客来分享一下我的经验。对于数学和编程爱好者来说&#xff0c;Matlab 是一个非常有用的工具。我自己在数学实验和数学建模竞赛中也经常使用它。那么&#xff0c;…

【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用

【JavaEE】 AOP&#xff08;1&#xff09; 文章目录 【JavaEE】AOP&#xff08;1&#xff09;1. Spring AOP 是什么1.1 AOP 与 Spring AOP1.2 没有AOP的世界是怎样的1.3 AOP是什么 2. Spring AOP 框架的学习2.1 AOP的组成2.1.1 Aspect 切面2.1.2 Pointcut 切点2.1.3 Advice 通知…

【广州华锐互动】VR高校虚拟实验教学平台提供丰富的资源支持,提高教学效果

随着科技的不断进步&#xff0c;虚拟现实(VR)技术已经逐渐渗透到各个领域&#xff0c;其中包括教育。 广州华锐互动利用VR虚拟现实技术打造的VR高校虚拟实验教学平台&#xff0c;是一种新型的教学工具&#xff0c;它提供了一个在线的教学资源管理平台&#xff0c;包含教学平台、…

深度学习在自然语言处理中的十大应用领域

文章目录 1. 机器翻译2. 文本分类3. 命名实体识别4. 问答系统5. 文本生成6. 情感分析7. 语言生成与处理8. 信息检索与摘要9. 文本纠错与修复10. 智能对话系统总结 &#x1f389;欢迎来到AIGC人工智能专栏~深度学习在自然语言处理中的十大应用领域 ☆* o(≧▽≦)o *☆嗨~我是IT陈…

Git企业开发控制理论和实操-从入门到深入(七)|企业级开发模型

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…