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://xiahunao.cn/news/1619823.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

“黑客”入门学习之“单机游戏外挂原理与实现”

“黑客”入门学习之“单机游戏外挂原理与实现”&#xff08;文末全套黑客资料教程&#xff09; 昨天给小伙伴们分享了一篇"游戏外挂原理与实现"的文章&#xff0c;小伙伴们很热情&#xff0c;反响很好&#xff0c;好多朋友私信我&#xff0c;或者直接回复我"写…

PMP P-03 Scope Management

范围管理&#xff1a;要做多少事情&#xff0c;内容

数据生成 | MATLAB实现GAN生成对抗网络结合SVM支持向量机的数据生成

数据生成 | MATLAB实现GAN生成对抗网络结合SVM支持向量机的数据生成 目录 数据生成 | MATLAB实现GAN生成对抗网络结合SVM支持向量机的数据生成生成效果基本描述程序设计参考资料 生成效果 基本描述 数据生成 | MATLAB实现GAN生成对抗网络结合SVM支持向量机的数据生成。 生成对抗…

代码随想录算法训练营第四十七天|LeetCode 382,115

目录 LeetCode 392.判断子序列 动态规划五步曲&#xff1a; 1.确定dp[i][j]的含义 2.找出递推公式 3.初始化dp数组 4.确定遍历顺序 5.打印dp数组 LeetCode 115.不同的子序列 动态规划五步曲&#xff1a; 1.确定dp[i][j]的含义 2.找出递推公式 3.初始化dp数组 4.确定遍历顺序 …

压缩包密码的破解

给压缩包添加密码 解密 将压缩包的加密信息放入新建的文本文档 zip2john 123.zip > mima.txt 使用john解密 john mima.txt john的密码字典路径 cd /etc/share/john ls 查看有多少行密码

压缩包解压密码怎么破

从网上下载的资源大多数都是以压缩包形式被下载下来&#xff0c;我们需要通过解压压缩包拿到我们想要的文件&#xff0c;但是有时候可能会遇到解压压缩包的时候需要密码的情况&#xff0c;那压缩包解压秘密该怎么破解呢&#xff1f;如果文件资源对你来说很重要的话&#xff0c;…

Linux系统:CentOS 7 CA证书服务器部署

目录 一、理论 1.CA认证中心 2.CA证书服务器部署 二、实验 1. CA证书服务器部署 一、理论 1.CA认证中心 &#xff08;1&#xff09;概念 CA &#xff1a;CertificateAuthority的缩写&#xff0c;通常翻译成认证权威或者认证中心&#xff0c;主要用途是为用户发放数字证…

D - President - 背包dp

分析&#xff1a; 需要让所有x大于y的对应的z的总数大于z总共的数量的一半&#xff0c;找最小需要转化的数量&#xff0c;那么可以转化为01背包问题&#xff0c;z作为体积&#xff0c;每组的x和y都可以计算出一个值表示需不需要转化&#xff0c;作为背包价值&#xff0c;如果x大…

利用阿里云服务器公网IP+FRP搭建内网穿透

1 必要条件&#xff1a; 一台公网IP服务器&#xff0c;这里采用阿里云ECS服务器。 此处将IP定义为:serverA-IP 2 服务器下载代码&#xff1a; # mkdir /data # cd /data # git clone https://github.com/fatedier/frp.git # cd frp3 编译代码 编译需要时间 # make go fmt .…

计算机组成原理学习笔记-精简复习版

一、计算机系统概述 计算机系统硬件软件 计算机硬件的发展&#xff1a; 第一代计算机&#xff1a;(使用电子管)第二代计算机&#xff1a;(使用晶体管)第三代计算机&#xff1a;(使用较小规模的集成电路)第四代计算机&#xff1a;(使用较大规模的集成电路) 冯诺依曼体系结构…

FLASH 停止后 IE无法使用

WINDOWS8 WINDOWS10系统下的IE10或者IE11出现 解决方法&#xff1a;打开https://www.flash.cn/

win10 家庭版无法使用IE浏览器

升级window10&#xff08;家庭版之后&#xff09;之后打开IE浏览器&#xff0c;打开照片都提示无法使用内置管理员账户打开&#xff0c;网上查看说打开secpol.msc&#xff0c;提示系统不存在该文件&#xff0c;经过一波周折之后&#xff0c;发现了一个快速解决的方案&#xff0…

单片机基础知识 06 (中断-2)

一. 定时器中断概念 51单片机的内部有两个16位可编程的定时器/计数器&#xff0c;即定时器T0和定时器T1。 52单片机内部多一个T2定时器/计数器。 定时器/计数器的实质是加1计数器&#xff08;16位&#xff09;&#xff0c;由高8位和低8位两个寄存器组成。 TMOD是定时器/计数器…

八大排序算法 (python版本)

八大排序算法 个人学习笔记 如有问题欢迎指正交流快速排序经常考&#xff0c; 如果只掌握一个排序算法的话&#xff0c;首选快速排序算法 八大排序算法通常指的是以下八种经典排序算法&#xff1a; 1. 冒泡排序 (Bubble Sort) 使用场景&#xff1a;适用于小规模数据的排序&a…

Ubuntu 3D桌面

转自http://forum.ubuntu.org.cn/viewtopic.php?f94&t140531 [2010年8月17日更新] Ubuntu Linux 3D桌面完全教程&#xff0c;显卡驱动安装方法&#xff0c;compiz特效介绍&#xff0c;常见问题解答。 本教程的前身是一善鱼编写并发布在Ubuntu中文论坛forum.ubuntu.org.cn3…

xendesktop更新计算机,XenDesktop7.12发布Win10周年更新版桌面

在上一篇XenCenter配置的资源池的基础上&#xff0c;本篇将使用该资源池作为基础环境搭建XenDesktop7.12发布Win10周年更新版桌面&#xff0c;XenDesktop7.12是上个月(2016年12月)才发布的版本&#xff0c;是目前最新版。本篇的主要内容包括&#xff1a;XenDesktop7.12安装、创…

2020网吧无盘服务器配置,云更新 2020.5.15.14195_x64 | 专业网吧维护

重点功能&#xff1a; 1.增强软件安全性 2.优化无盘启动&#xff0c;支持BIOS、UEFI、通用镜像自动适配 3.增加镜像转换工具&#xff0c;支持将BIOS或UEFI镜像转换为通用镜像 4.增加显卡纹理质量和低延迟模式的设置 其他更新&#xff1a; 5.优化显示器信息采集功能 6.优化控制台…

BM80 买卖股票的最好时机(一)

目录 1.题目描述 2.题目分析 3.编写代码 4.总结 这是牛客网上的一道题目 1.题目描述 题目链接&#xff1a;买卖股票的最好时机(一)_牛客题霸_牛客网 (nowcoder.com) 2.题目分析 我们看到这个题目中一个数组表示每一天的股价&#xff0c;那么最大利润怎么算呢&#xff0c…

MFC界面库

好东西&#xff0c;果断收藏 刚开始用C做界面的时候&#xff0c;根本不知道怎么用简陋的MFC控件做出比较美观的界面&#xff0c;后来就开始逐渐接触到BCG Xtreme ToolkitPro v15.0.1&#xff0c;Skin,等界面库&#xff0c;以及一些网友自己写的界面库&#xff0c;开始对于C软件…

android开源库合集

android开源库合集 1、阿里巴巴开源的自定义viewpager&#xff0c;支持多重动画&#xff0c;横向纵向&#xff0c;多页面显示 项目地址&#xff1a;https://github.com/alibaba/UltraViewPager 2、android版本更新功能。使用retrfit2 rxjava2 okhttp3实现多文件多线程下载&am…