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

域名制作网站吗网站建设外包公司招聘

域名制作网站吗,网站建设外包公司招聘,惠州网站建设创业,朝阳网站建设多少钱文章目录一、背景二、什么是JUC#xff1f;三、JUC框架结构四、JUC框架概述五、JUC中常用类汇总六、相关名词进程和线程进程线程创建线程的几种常见的方式并发和并行用户线程和守护线程七、synchronized 作用范围#xff1a;八、Lock锁(重点)什么是 Lock锁类型Lock接口lock()… 文章目录一、背景二、什么是JUC三、JUC框架结构四、JUC框架概述五、JUC中常用类汇总六、相关名词进程和线程进程线程创建线程的几种常见的方式并发和并行用户线程和守护线程七、synchronized 作用范围八、Lock锁(重点)什么是 Lock锁类型Lock接口lock()、unlocknewConditionReentrantLock 可重入锁ReadWriteLock 读写锁案例Lock 与的 Synchronized 区别九、Callable 接口前言概述实现十、Future 接口概述实现FutureTaskFutureTask介绍FutureTask应用场景及注意事项使用 Callable 和 Future十一、JUC三大常用工具类CountDownLatch减计数器概述案例小结CyclicBarrier加法计数器概述案例小结Semaphore 信号灯概述案例小结简单讲述 | Phaser ExchangerPhaserExchanger十二、总结一、背景 如果你对多线程没什么了解那么从入门模块开始。 如果你已经入门了多线程知道基础的线程创建、死锁、synchronized、lock等那么从juc模块开始。 新手学技术、老手学体系高手学格局。 二、什么是JUC JUC实际上就是我们对于jdk中java.util .concurrent 工具包的简称其结构如下 这个包下都是Java处理线程相关的类自jdk1.5后出现。目的就是为了更好的支持高并发任务。让开发者进行多线程编程时减少竞争条件和死锁的问题 JUC主要是指JDK8中java.util.concurrent里提供的一系列线程并发工具但是线程并发的问题远不止几个工具这么简单。要学习工具使用更要能深入理解工具的原理以及处理线程并发问题的思路。 三、JUC框架结构 UC是包的简称JUC可能也是Java核心里最难的一块儿JUC指的是Java的并发工具包里边提供了各种各样的控制同步和线程通信的工具类。学习JUC之前最重要的是了解JUC的结构是什么样的。就如同Java的集合框架的结构一样JUC也有自己框架结构只是往往被大家忽略笔者就简单的梳理了下JUC的框架结构JUC的框架结构不同于集合它并非是实现继承框架结构。 四、JUC框架概述 JUC框架的底层在Java代码里是Unsafe而Unsafe是底层Jvm的实现。有了Unsafe的支持实现了一些了支持原子型操作的Atomic类然后上层才有了我们熟知的AQS和LockSupport等类。有了这些之后才是各种读写锁各种线程通信以及同步工具的实现类。 五、JUC中常用类汇总 JUC的atomic包下运用了CAS的AtomicBoolean、AtomicInteger、AtomicReference等原子变量类 JUC的locks包下的AbstractQueuedSynchronizerAQS以及使用AQS的ReentantLock显式锁、ReentrantReadWriteLock 附运用了AQS的类还有 Semaphore、CountDownLatch、ReentantLock显式锁、ReentrantReadWriteLock JUC下的一些同步工具类 CountDownLatch闭锁、Semaphore信号量、CyclicBarrier栅栏、FutureTask JUC下的一些并发容器类 ConcurrentHashMap、CopyOnWriteArrayList 读写分离 CopyOnWriteArrayList 写操作在一个复制的数组上进行读操作还是在原始数组中进行读写分离互不影响。 写操作需要加锁防止并发写入时导致写入数据丢失。 写操作结束之后需要把原始数组指向新的复制数组。 JUC下的一些Executor框架的相关类 线程池的工厂类-Executors 线程池的实现类-ThreadPoolExecutor/ForkJoinPool JUC下的一些阻塞队列实现类 ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue tools工具类又叫信号量三组工具类包含有 CountDownLatch闭锁 是一个同步辅助类在完成一组正在其他线程中执行的操作之前它允许一个或多个线程一直等待 CyclicBarrier栅栏 之所以叫barrier是因为是一个同步辅助类允许一组线程互相等待直到到达某个公共屏障点 并且在释放等待线程后可以重用。 Semaphore信号量 是一个计数信号量它的本质是一个“共享锁“。信号量维护了一个信号量许可集。线程可以通过调用 acquire()来获取信号量的许可当信号量中有可用的许可时线程能获取该许可否则线程必须等待直到有可用的许可为止。 线程可以通过release()来释放它所持有的信号量许可。 executor(执行者)是Java里面线程池的顶级接口但它只是一个执行线程的工具真正的线程池接口是ExecutorService里面包含的类有 ScheduledExecutorService 解决那些需要任务重复执行的问题ScheduledThreadPoolExecutor 周期性任务调度的类实现 atomic(原子性包)是JDK提供的一组原子操作类包含有AtomicBoolean、AtomicInteger、AtomicIntegerArray等原子变量类他们的实现原理大多是持有它们各自的对应的类型变量value而且被volatile关键字修饰了。这样来保证每次一个线程要使用它都会拿到最新的值。 locks锁包是JDK提供的锁机制相比synchronized关键字来进行同步锁功能更加强大它为锁提供了一个框架该框架允许更灵活地使用锁包含的实现类有 ReentrantLock 它是独占锁是指只能被独自占领即同一个时间点只能被一个线程锁获取到的锁。 ReentrantReadWriteLock 它包括子类ReadLock和WriteLock。ReadLock是共享锁而WriteLock是独占锁。 LockSupport 它具备阻塞线程和解除阻塞线程的功能并且不会引发死锁。 collections(集合类)主要是提供线程安全的集合 比如 ArrayList对应的高并发类是CopyOnWriteArrayList HashSet对应的高并发类是 CopyOnWriteArraySet HashMap对应的高并发类是ConcurrentHashMap等等 六、相关名词 进程和线程 进程 概述 进程Process 是计算机中的程序关于某数据集合上的一次运行活动是系统进行资源分配和调度的基本单位是操作系统结构的基础。 在当代面向线程设计的计算机结构中进程是线程的容器。程序是指令、数据及其组织形式的描述进程是程序的实体。 定义 狭义定义进程是正在运行的程序的实例an instance of a computer program that is being executed。 广义定义进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元在传统的操作系统中进程既是基本的分配单元也是基本的执行单元。 线程 线程thread 是操作系统能够进行运算调度的最小单位。它被包含在进程之 中是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流 一个进程中可以并发多个线程每条线程并行执行不同的任务。 线程是独立调度和分派的基本单位。同一进程中的多条线程将共享该进程中的全部系统资源。一个进程可以有很多线程每条线程并行执行不同的任务。可并发执行。 当然我们Java都知道的线程的同步是Java多线程编程的难点因为要给哪些地方是共享资源竞争资源什么时候要考虑同步等都是编程中的难点。 创建线程的几种常见的方式 通过实现Runnable接口来创建Thread线程通过继承Thread类来创建一个线程通过实现Callable接口来创建Thread线程 备注详细看我写的上一篇文章Java实现多线程的4种方式 并发和并行 在了解并发和并行之前让我们先来看一看串行是什么样的吧。 串行模式 串行模式即表示所有任务都是按先后顺序进行。串行是一次只能取的一个任务并执行这个任务。举个生活中的小例子就是在火车站买票今天只开放这一个窗口卖票那么我们只有等到前面的人都买了才能轮到我们去买。即按先后顺序买到票。 并行模式 概述一组程序按独立异步的速度执行无论从微观还是宏观程序都是一起执行的。对比地并发是指:在同一个时间段内两个或多个程序执行有时间上的重叠(宏观上是同时,微观上仍是顺序执行)。并行模式并行意味着可以同时取得多个任务并同时去执行所取得的这些任务。 我们还是用上面那个例子还是在买票以前是只有一个窗口卖票但是近几年发展起来了现在有五个窗口卖票啦大大缩短了人们买票的时间。并行模式相当于将长长的一条队列划分成了多条短队列所以并行缩短了任务队列的长度。不过并行的效率一方面受多进程/线程编码的好坏的影响另一方面也受硬件角度上的CPU的影响。 并发 并发并发指的是多个程序可以同时运行的一种现象并发的重点在于它是一种现象,并发描述的是多进程同时运行的现象。但真正意义上一个单核心CPU任一时刻都只能运行一个线程。所以此处的同时运行表示的不是真的同一时刻有多个线程运行的现象这是并行的概念而是提供了一种功能让用户看来多个程序同时运行起来了但实际上这些程序中的进程不是一直霸占 CPU 的而是根据CPU的调度执行一会儿停一会儿。 小小的总结一下 并发即同一时刻多个线程在访问同一个资源多个线程对一个点 例子秒杀活动、12306抢回家的票啦、抢演唱会的票… 并行多个任务一起执行之后再汇总 例子电饭煲煮饭、用锅炒菜两个事情一起进行(最后我们一起干饭啦干饭啦) 用户线程和守护线程 用户线程指不需要内核支持而在用户程序中实现的线程其不依赖于操作系统核心应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。 守护线程是指在程序运行的时候在后台提供一种通用服务的线程用来服务于用户线程不需要上层逻辑介入当然我们也可以手动创建一个守护线程。(用白话来说就是守护着用户线程当用户线程死亡守护线程也会随之死亡) 比如垃圾回收线程就是一个很称职的守护者并且这种线程并不属于程序中不可或缺的部分。因此当所有的非守护线程结束时程序也就终止了同时会杀死进程中的所有守护线程。反过来说只要任何非守护线程还在运行程序就不会终止。 用一个简单代码来模拟一下 未设置为守护线程时主线程执行完成了但是我们自己创建的线程仍然未结束。 设置为守护线程后明显可以看到当主线程执行完成后我们设置为守护线程的那个线程也被强制结束了。 setDaemon就是设置为是否为守护线程。 七、synchronized 作用范围 synchronized 是 Java 中的关键字是一种同步锁。它修饰的对象有以下几种 修饰某一处代码块被修饰的代码块称为同步语句块。作用范围就是{}之间。作用的对象是调用这个代码块的对象。 synchronized (this){System.out.println(同步代码块 ); }修饰在方法上被修饰的方法就称为同步方法。作用范围则是整个方法。作用的对象则是调用这个方法的对象。 public synchronized void sale() {}注synchronized 关键字不能被继承如果父类中某方法使用了synchronized 关键字字类又正巧覆盖了此时字类默认情况下是不同步的必须显示的在子类的方法上加上才可。当然如果在字类中调用父类中的同步方法这样虽然字类并没有同步方法但子类调用父类的同步方法子类方法也相当同步了。 修饰某个静态的方法其作用的范围是整个静态方法作用的对象是这个类的所有对象。 public static synchronized void test(){}修饰某个类其作用的范围是 synchronized 后面括号括起来的部分作用的对象是这个类的所有对象。 class Ticket {public void sale() {synchronized (Ticket.class) {}} }八、Lock锁(重点) 什么是 Lock Lock 锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。 锁类型 可重入锁在执行对象中所有同步方法不用再次获得锁 可中断锁在等待获取锁过程中可中断 公平锁 按等待获取锁的线程的等待时间进行获取等待时间长的具有优先获取锁权利 读写锁对资源读取和写入的时候拆分为2部分处理读的时候可以多线程一起读写的时候必须同步地写 Lock接口 public interface Lock {void lock(); //获得锁。/**除非当前线程被中断否则获取锁。如果可用则获取锁并立即返回。如果锁不可用则当前线程将出于线程调度目的而被禁用并处于休眠状态直到发生以下两种情况之一锁被当前线程获取 要么其他一些线程中断当前线程支持中断获取锁。如果当前线程在进入此方法时设置其中断状态 要么获取锁时中断支持中断获取锁*/void lockInterruptibly() throws InterruptedException; /**仅在调用时空闲时才获取锁。如果可用则获取锁并立即返回值为true 。 如果锁不可用则此方法将立即返回false值。*/boolean tryLock();//比上面多一个等待时间 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 解锁void unlock(); //返回绑定到此Lock实例的新Condition实例。Condition newCondition(); 。 }下面讲几个常用方法的使用。 lock()、unlock lock是最常用的方法之一作用就是获取锁如果锁已经被其他线程获得则当前线程将被禁用以进行线程调度并处于休眠状态等待直到获取锁。 如果使用到了lock的话那么必须去主动释放锁就算发生了异常也需要我们主动释放锁因为lock并不会像synchronized一样被自动释放。所以使用lock的话必须是在try{}catch(){}中进行并将释放锁的代码放在finally{}中以确保锁一定会被释放以防止死锁现象的发生。 unlock的作用就是主动释放锁。 lock接口的类型有好几个实现类这里是随便找了个。 Lock lock new ReentrantLock(); try {lock.lock();System.out.println(上锁了); }catch (Exception e){e.printStackTrace(); }finally {lock.unlock();System.out.println(解锁了); }newCondition 关键字 synchronized 与 wait()/notify()这两个方法一起使用可以实现等待/通知模式 Lock 锁的 newContition()方法返回 Condition 对象Condition 类 也可以实现等待/通知模式。 用 notify()通知时JVM 会随机唤醒某个等待的线程 使用 Condition 类可以 进行选择性通知 Condition 比较常用的两个方法 await()会使当前线程等待,同时会释放锁,当等到其他线程调用signal()方法时,此时这个沉睡线程会重新获得锁并继续执行代码在哪里沉睡就在哪里唤醒。signal()用于唤醒一个等待的线程。 注意在调用 Condition 的 await()/signal()方法前也需要线程持有相关 的 Lock 锁调用 await()后线程会释放这个锁在调用singal()方法后会从当前 Condition对象的等待队列中唤醒一个线程后被唤醒的线程开始尝试去获得锁 一旦成功获得锁就继续往下执行。 在这个地方我们举个例子来用代码写一下 这里就不举例synchronized 实现了道理都差不多。 例子我们有两个线程实现对一个初始值是0的number变量一个线程当number 0时 对number值1另外一个线程当number 1时对number-1。 class Share {private Integer number 0;private ReentrantLock lock new ReentrantLock();private Condition newCondition lock.newCondition();// 1 的方法public void incr() {try {lock.lock(); // 加锁while (number ! 0) {newCondition.await();//沉睡}number;System.out.println(Thread.currentThread().getName() :: number);newCondition.signal(); //唤醒另一个沉睡的线程 } catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}// -1 的方法public void decr() {try {lock.lock();while (number ! 1) {newCondition.await();}number--;System.out.println(Thread.currentThread().getName() :: number);newCondition.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}} }public class LockDemo2 {public static void main(String[] args) {Share share new Share();new Thread(()-{for (int i0;i10;i){share.incr();}},AA).start();new Thread(()-{for (int i0;i10;i){share.decr();}},BB).start();/*** AA::1* BB::0* AA::1* BB::0* .....*/ } }ReentrantLock 可重入锁 ReentrantLock意思是“可重入锁”。ReentrantLock 是唯一实现了 Lock 接口的类并且 ReentrantLock 提供了更 多的方法。 可重入锁什么是 “可重入”可重入就是说某个线程已经获得某个锁可以再次获取锁而不会出现死锁 package com.crush.juc02;import java.util.Random; import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockDemo {public static void main(String[] args) {ReentrantLock lock new ReentrantLock();new Thread(new Runnable() {Overridepublic void run() {try {lock.lock();System.out.println(第1次获取锁这个锁是 lock);for (int i 2;i11;i){try {lock.lock();System.out.println(第 i 次获取锁这个锁是 lock);try {Thread.sleep(new Random().nextInt(200));} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();// 如果把这里注释掉的话那么程序就会陷入死锁当中。}}} finally {lock.unlock();}}}).start();new Thread(new Runnable() {Overridepublic void run() {try {lock.lock();System.out.println(这里是为了测试死锁而多写一个的线程);} finally {lock.unlock();}}}).start();} } /*** 第1次获取锁这个锁是java.util.concurrent.locks.ReentrantLock6b5fde1f[Locked by thread Thread-0]* 第2次获取锁这个锁是java.util.concurrent.locks.ReentrantLock6b5fde1f[Locked by thread Thread-0]* 第3次获取锁这个锁是java.util.concurrent.locks.ReentrantLock6b5fde1f[Locked by thread Thread-0]* ...*/死锁的话程序就无法停止直到资源耗尽或主动终止。 代码中也稍微提了一下死锁的概念在使用Lock中必须手动解锁不然就会可能造成死锁的现象。 ReadWriteLock 读写锁 ReadWriteLock 也是一个接口在它里面只定义了两个方法 public interface ReadWriteLock {// 获取读锁Lock readLock();// 获取写锁Lock writeLock(); }分为一个读锁一个写锁将读写进行了分离使可以多个线程进行读操作从而提高了效率。 ReentrantReadWriteLock 实现了 ReadWriteLock 接口。里面提供了更丰富的方法当然最主要的还是获取写锁writeLock和读锁readLock。 案例 假如多个线程要进行读的操作我们用Synchronized 来实现的话。 public class SynchronizedDemo2 {public static void main(String[] args) {final SynchronizedDemo2 test new SynchronizedDemo2();new Thread(()-{test.get(Thread.currentThread());}).start();new Thread(()-{test.get(Thread.currentThread());}).start();}public synchronized void get(Thread thread) {long start System.currentTimeMillis();while(System.currentTimeMillis() - start 1) {System.out.println(thread.getName()正在进行读操作);}System.out.println(thread.getName()读操作完毕);} } /*** 输出* Thread-0正在进行读操作* Thread-0读操作完毕* Thread-1正在进行读操作* Thread-1正在进行读操作* Thread-1正在进行读操作* ....* Thread-1读操作完毕*/改成读写锁之后 public class SynchronizedDemo2 {private ReentrantReadWriteLock rwl new ReentrantReadWriteLock();public static void main(String[] args) {final SynchronizedDemo2 test new SynchronizedDemo2();new Thread(()-{test.get2(Thread.currentThread());}).start();new Thread(()-{test.get2(Thread.currentThread());}).start();}public void get2(Thread thread) {rwl.readLock().lock();try {long start System.currentTimeMillis();while(System.currentTimeMillis() - start 1) {System.out.println(thread.getName()正在进行读操作);}System.out.println(thread.getName()读操作完毕);} finally {rwl.readLock().unlock();}} } /*** 输出* Thread-0正在进行读操作* Thread-0读操作完毕* Thread-1正在进行读操作* Thread-1读操作完毕*/结论改用读写锁后 线程1和线程2 同时在读可以感受到效率的明显提升。 注意: 若此时已经有一个线程占用了读锁此时其他线程申请读锁是可以的但是若此时其他线程申请写锁则只有等待读锁释放才能成功获得。若此时已经有一个线程占用了写锁那么此时其他线程申请写锁或读锁都只有持有写锁的线程释放写锁才能成功获得。 Lock 与的 Synchronized 区别 类别synchronizedLock存在层次Java的关键字在jvm层面上是一个接口锁的获取假设A线程获得锁B线程等待。如果A线程阻塞B线程会一直等待分情况而定Lock有多个锁获取的方式具体下面会说道大致就是可以尝试获得锁线程可以不用一直等待锁的释放1、当 synchronized 方法或者 synchronized 代码块执行完之后 系统会自动让线程释放对锁的占用 不需要手动释放锁2、若线程执行发生异常jvm会让线程释放锁在finally中必须释放锁不然容易造成线程死锁现象 需要手动释放锁锁状态无法判断可以判断锁类型锁类型可重入 可判断 可公平两者皆可性能前提大量线程情况下 同步效率较低前提大量线程情况下 同步效率比synchronized高的多 Lock可以提高多个线程进行读操作的效率。 九、Callable 接口 前言 在上上篇文章中创建线程那个小角落提到了这个但是当时只是匆匆忙忙讲了一下。到这里再全面性的讲解一下。 我们以前使用实现Runnable接口的方式来创建线程但是Runnable的run() 存在一个缺陷问题就是不能将执行完的结果返回。 Java就是为了能够实现这个功能在jdk1.5中提出了Callable接口。 概述 Callable 接口位于java.util.concurrent包下。 FunctionalInterface public interface CallableV {V call() throws Exception; //计算结果如果无法计算则抛出异常。 }Callable 类似于Runnable 接口但Runnable 接口中的run方法不会返回结果并且也无法抛出经过检查的异常但是Callable中call方法能够返回计算结果并且也能够抛出经过检查的异常。 实现 通过实现Callable接口创建线程详细步骤Runnable直接看代码就知道了哈。 创建实现Callable接口的类SomeCallable 创建一个类对象Callable oneCallable new SomeCallable(); 由Callable创建一个FutureTask对象FutureTask futureTask new FutureTask(oneCallable); 注释FutureTask是一个包装器它通过接受Callable来创建它同时实现了Future和Runnable接口。 由FutureTask创建一个Thread对象Thread oneThread new Thread(futureTask); 启动线程oneThread.start(); public class Demo1 {public static void main(String[] args) {new Thread(new RunnableDemo1(),AA).start();FutureTaskInteger futureTask new FutureTask(new CallableDemoInteger());new Thread(futureTask,BB).start();// 在线程执行完后我们可以通过futureTask的get方法来获取到返回的值。System.out.println(futureTask.get());} } class RunnableDemo1 implements Runnable{Overridepublic void run() {System.out.println(Thread.currentThread().getName()::通过实现Runnable来执行任务);} } class CallableDemoInteger implements Callablejava.lang.Integer {Overridepublic java.lang.Integer call() throws Exception {System.out.println(Thread.currentThread().getName()::通过实现Callable接口来执行任务并返回结果);return 1024;} } /*** AA::通过实现Runnable来执行任务* BB::通过实现Callable接口来执行任务并返回结果* 1024*/这里之所以要转成 FutureTask 放进 Thread中去是因为Callable 本身与Thread没有关系通过FutureTask 才能和Thread产生联系。 十、Future 接口 概述 Future 接口同样位于java.util.concurrent包下。 Future接口提供方法来检测任务是否被执行完等待任务执行完获得结果也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程序执行超时的关键。 public interface FutureV {boolean cancel(boolean mayInterruptIfRunning); //尝试取消此任务的执行。boolean isCancelled();//如果此任务在正常完成之前被取消则返回true boolean isDone(); //如果此任务完成则返回true 。 完成可能是由于正常终止、异常或取消——在所有这些情况下此方法将返回true V get() throws InterruptedException, ExecutionException; //获得任务计算结果V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;//可等待多少时间去获得任务计算结果 }实现 Future模式通俗点来描述就是我有一个任务提交给了FutureFuture替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后我就便可以从Future那儿取出结果。 public class Demo1 {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTaskInteger futureTask new FutureTask(new CallableDemoInteger());new Thread(futureTask,BB).start();System.out.println(futureTask.get());// 我们来测试一下任务是否已经完成System.out.println(futureTask.isDone());} } class CallableDemoInteger implements Callablejava.lang.Integer {Overridepublic java.lang.Integer call() throws Exception {System.out.println(Thread.currentThread().getName()::通过实现Callable接口来执行任务并返回结果);return 1024;} }Future 用于存储从另一个线程获得的结果。如果只是简单创建线程直接使用Runnable就可以想要获得任务返回值就用Future。 FutureTask FutureTask介绍 位于 java.util.concurrent包下。 可取消的异步计算。 此类提供Future的基本实现具有启动和取消计算、查询以查看计算是否完成以及检索计算结果的方法。 计算完成后才能检索结果 如果计算尚未完成 get方法将阻塞。 一旦计算完成就不能重新开始或取消计算除非使用runAndReset调用计算。结构图 FutureTask实现了 Runnable 和 Future接口并方便地将两种功能组合在一起。并且通过构造函数提供Callable来创建FutureTask就可以提供给Thread来创建线程啦。 FutureTask有以下三种状态 未启动状态还未执行run方法。已启动状态已经在执行run方法。完成状态已经执行完run()方法或者被取消了亦或者方法中发生异常而导致中断结束。 FutureTask应用场景及注意事项 应用场景 在主线程执行那种比较耗时的操作时但同时又不能去阻塞主线程时就可以将这样的任务交给FutureTask对象在后台完成然后等之后主线程需要的时候就可以直接get()来获得返回数据或者通过isDone来获得任务的状态。一般FutureTask多应用于耗时的计算这样主线程就可以把一个耗时的任务交给FutureTask然后等到完成自己的任务后再去获取计算结果 注意 仅在计算完成时才能检索结果如果计算尚未完成则阻塞 get 方法。一旦计 算完成就不能再重新开始或取消计算。get 方法而获取结果只有在计算完成时获取否则会一直阻塞直到任务转入完成状态然后会返回结果或者抛出异常。因为只会计算一次,因此通常get方法放到最后。 使用放在下一小节啦 使用 Callable 和 Future 这里的使用其实在上文已经提到过了这里就将其更完善一些吧。 public class CallableDemo2 {public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {CallableAndFutureTest callableAndFutureTest new CallableAndFutureTest();FutureTaskString task new FutureTask(callableAndFutureTest);new Thread(task).start();// System.out.println(尝试取消任务传true表示取消任务false则不取消任务::task.cancel(true));System.out.println(判断任务是否已经完成::task.isDone());//结果已经计算出来则立马取出来如若摸没有计算出来则一直等待直到结果出来或任务取消或发生异常。System.out.println(阻塞式获取结果::task.get());System.out.println(在获取结果时给定一个等待时间如果超过等待时间还未获取到结果则会主动抛出超时异常::task.get(2, TimeUnit.SECONDS));} }class CallableAndFutureTest implements CallableString {Overridepublic String call() throws Exception {String str;for (int i0;i10;i){strString.valueOf(i);Thread.sleep(100);}return str;} }十一、JUC三大常用工具类 CountDownLatch减计数器 概述 CountDownLatch位于 java.util.concurrent包下。 CountDownLatch是一个同步辅助类允许一个或多个线程等待一直到其他线程执行的操作完成后再执行。 CountDownLatch是通过一个计数器来实现的计数器的初始值是线程的数量。每当有一个线程执行完毕后然后通过 countDown 方法来让计数器的值-1当计数器的值为0时表示所有线程都执行完毕然后继续执行 await 方法 之后的语句即在锁上等待的线程就可以恢复工作了。 CountDownLatch中主要有两个方法 countDown 递减锁存器的计数如果计数达到零则释放所有等待的线程。如果当前计数大于零则递减。 如果新计数为零则为线程调度目的重新启用所有等待线程。如果当前计数为零则什么也不会发生。 public void countDown() {sync.releaseShared(1); }await 使当前线程等待直到闩锁倒计时为零除非线程被中断。如果当前计数为零则此方法立即返回。即await 方法阻塞的线程会被唤醒继续执行如果当前计数大于零则当前线程出于线程调度目的而被禁用并处于休眠状态 public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1); }案例 举个生活中的小例子 我们一寝室人去上课得等到1、2、3、4、5、6、7、8个人都出来才可以锁上寝室门吧。即当计数器值为0时就可以执行await的方法啦。 编码步骤 CountDownLatch countDownLatch new CountDownLatch(8);countDownLatch.countDown(); 一个线程出来一个人计数器就 -1countDownLatch.await(); 阻塞的等待计数器归零执行后续步骤 我们用代码来模拟一下这个例子哈 public class CountDownLatchDemo1 {public static void main(String[] args) {// 初始值8 有八个人需要出寝室门CountDownLatch countDownLatch new CountDownLatch(8);for (int i 1; i 8; i) {new Thread(() - {System.out.println(Thread.currentThread().getName() 出去啦);// 出去一个人计数器就减1countDownLatch.countDown();}, String.valueOf(i)).start();}try {countDownLatch.await(); // 阻塞等待计数器归零} catch (InterruptedException e) {e.printStackTrace();}// 阻塞的操作 计数器 numSystem.out.println(Thread.currentThread().getName() 寝室人都已经出来了关门向教室冲);} }小结 CountDownLatch使用给定的计数进行初始化。 由于调用了countDown方法每次-1 await方法会一直阻塞到当前计数达到零然后释放所有等待线程并且任何后续的await调用都会立即返回。 这是一种一次性现象——计数无法重置。 如果您需要重置计数的版本请考虑使用CyclicBarrier 。 CountDownLatch一个有用属性是它不需要调用countDown线程在继续之前等待计数达到零它只是阻止任何线程通过await,直到所有线程都可以通过。 CyclicBarrier加法计数器 概述 CyclicBarrier 看英文单词就可以看出大概就是循环阻塞的意思。所以还常称为循环栅栏。 CyclicBarrier 主要方法有: public class CyclicBarrier {private int dowait(boolean timed, long nanos); // 供await方法调用 判断是否达到条件 可以往下执行吗//创建一个新的CyclicBarrier,它将在给定数量的参与方线程等待时触发每执行一次CyclicBarrier就累加1达到了parties就会触发barrierAction的执行public CyclicBarrier(int parties, Runnable barrierAction) ;//创建一个新的CyclicBarrier 参数就是目标障碍数,它将在给定数量的参与方线程等待时触发每次执行 CyclicBarrier 一次障碍数会加一如果达到了目标障碍数才会执行 cyclicBarrier.await()之后的语句public CyclicBarrier(int parties) //返回触发此障碍所需的参与方数量。public int getParties()//等待直到所有各方都在此屏障上调用了await 。// 如果当前线程不是最后一个到达的线程那么它会出于线程调度目的而被禁用并处于休眠状态.直到所有线程都调用了或者被中断亦或者发生异常中断退出public int await()// 基本同上 多了个等待时间 等待时间内所有线程没有完成将会抛出一个超时异常public int await(long timeout, TimeUnit unit)//将障碍重置为其初始状态。 public void reset()}public CyclicBarrier(int parties)的构造方法第一个参数是目标障碍数每次执行 CyclicBarrier 一次障碍数会加一如果达到了目标障碍数才会执行 cyclicBarrier.await()之后 的语句。可以将 CyclicBarrier 理解为加 1 操作。 public CyclicBarrier(int parties, Runnable barrierAction) 的构造方法第一个参数是目标障碍数每次执行 CyclicBarrier 一次障碍数会加一如果达到了目标障碍数就会执行我们传入的Runnable 案例 我想大家多少玩过王者荣耀吧里面不是有个钻石夺宝吗抽201次必得荣耀水晶这次让我们用代码来模拟一下吧。 编程步骤 创建CyclicBarrier对象 CyclicBarrier cyclicBarrier new CyclicBarrier(count, new MyRunnable()); 编写业务代码 cyclicBarrier.await(); //在线程里面等待阻塞累加1,达到最大值count时触发我们传入进去MyRunnable执行。 public class CyclicBarrierDemo1 {public static void main(String[] args) {// 第一个参数目标障碍数 第二个参数一个Runnable任务当达到目标障碍数时就会执行我们传入的Runnable// 当我们抽了201次的时候就会执行这个任务。CyclicBarrier cyclicBarrier new CyclicBarrier(201,()-{System.out.println(恭喜你已经抽奖201次幸运值已满下次抽奖必中荣耀水晶);});for (int i1;i201;i){final int counti;new Thread(()-{System.out.println(Thread.currentThread().getName()抽奖一次);try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(i)).start();}}// 这行代码是重置计数cyclicBarrier.reset();// 这里是我又加了 一次循环 可以看到最后结果中输出了两次 恭喜你for (int i1;i201;i){final int counti;new Thread(()-{System.out.println(Thread.currentThread().getName()抽奖一次);try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(i)).start();} }小结 CyclicBarrier和CountDownLatch其实非常相似CyclicBarrier表示加法CountDownLatch表示减法。 区别还是有的 CyclicBarrier只能够唤醒一个任务CountDownLatch可以唤起多个任务。CyclicBarrier可以重置重新使用但是CountDownLatch的值等于0时就不可重复用了。 Semaphore 信号灯 概述 Semaphore信号量通常用于限制可以访问某些物理或逻辑资源的线程数。 使用场景 限制资源如抢位置、限流等。 案例 【例子】 不知道大家有没有过在网吧抢电脑打游戏的那种经历小时候平常便宜点的网吧都比较小而且也比较少特别多的人去去晚了的人就只有站在那里看等别人下机才能上网。 这次的例子就是网吧有十台高配置打游戏的电脑有20个小伙伴想要上网。 我们用代码来模拟一下 编程步骤 创建信号灯 Semaphore semaphore new Semaphore(10); // 5个位置 等待获取信号灯 semaphore.acquire();//等待获取许可证 业务代码 释放信号 semaphore.release();//释放资源,女朋友来找了下机下机陪女朋友去了那么就要释放这台电脑啦 public class SemaphoreDemo1 {public static void main(String[] args) {// 10台电脑Semaphore semaphore new Semaphore(10);// 20 个小伙伴想要上网for (int i 1; i 20; i) {new Thread(() - {try {//等待获取许可证semaphore.acquire();System.out.println(Thread.currentThread().getName() 抢到了电脑);//抢到的小伙伴迅速就开打啦 这里就模拟个时间哈TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();} finally {//打完几把游戏的小伙伴 女朋友来找了 溜啦溜啦 希望大家都有人陪伴System.out.println(女朋友来找Thread.currentThread().getName() 离开了);semaphore.release();//释放资源,离开了就要把电脑让给别人啦。}}, String.valueOf(i)).start();}} }小结 在获得一个项目之前每个线程必须从信号量中获得一个许可以保证一个项目可供使用。 当线程完成该项目时它会返回到池中并且将许可返回给信号量允许另一个线程获取该项目。 请注意调用acquire时不会持有同步锁因为这会阻止项目返回到池中。 信号量封装了限制访问池所需的同步与维护池本身一致性所需的任何同步分开。初始化为 1 的信号量并且使用时最多只有一个许可可用可以用作互斥锁。 这通常被称为二进制信号量因为它只有两种状态一种许可可用或零许可可用。 以这种方式使用时二进制信号量具有属性与许多java.util.concurrent.locks.Lock实现不同即“锁”可以由所有者以外的线程释放因为信号量没有所有权的概念。 这在某些特定上下文中很有用例如死锁恢复。此类的构造函数可以选择接受公平参数。 当设置为 false 时此类不保证线程获取许可的顺序。 当公平性设置为真时信号量保证调用任何acquire方法的线程被选择以按照它们对这些方法的调用的处理顺序先进先出FIFO获得许可。通常用于控制资源访问的信号量应初始化为公平的以确保没有线程因访问资源而饿死。 当使用信号量进行其他类型的同步控制时非公平排序的吞吐量优势通常超过公平性考虑。内存一致性影响在调用“释放”方法如release()之前线程中的操作发生在另一个线程中成功的“获取”方法如acquire()之后的操作之前。 简单讲述 | Phaser Exchanger Phaser Phaser一种可重用的同步屏障功能上类似于CyclicBarrier和CountDownLatch但使用上更为灵活。非常适用于在多线程环境下同步协调分阶段计算任务Fork/Join框架中的子任务之间需同步时优先使用Phaser //默认的构造方法初始化注册的线程数量为0,可以动态注册 Phaser(); //指定了线程数量的构造方法 Phaser(int parties); //添加一个注册者 向此移相器添加一个新的未到达方。 如果正在进行对onAdvance调用则此方法可能会在返回之前等待其完成。 register(); //添加指定数量的注册者 将给定数量的新未到达方添加到此移相器(移相器就是Phaser)。 bulkRegister(int parties); // 到达屏障点直接执行 无需等待其他人到达。 arrive(); //到达屏障点后也必须等待其他所有注册者到达这个屏障点才能继续下一步 arriveAndAwaitAdvance(); //到达屏障点把自己注销了不用等待其他的注册者到达 arriveAndDeregister(); //多个线程达到注册点之后会回调这个方法可以做一些逻辑的补充 onAdvance(int phase, int registeredParties);package com.crush.juc05;import java.util.concurrent.Phaser;public class PhaserDemo {private static Phaser phaser new MyPhaser();//自定义一个移相器来自定义输出static class MyPhaser extends Phaser {/*** deprecated 在即将到来的阶段提前时执行操作并控制终止的可覆盖方法。 此方法在推进此移相器的一方到达时调用当所有其他等待方处于休眠状态时。* 如果此方法返回true 则此移相器将在提前时设置为最终终止状态并且对isTerminated后续调用将返回 true。* param phase 进入此方法的当前阶段号在此移相器前进之前* param registeredParties 当前注册方的数量* return*/Overrideprotected boolean onAdvance(int phase, int registeredParties) {if (phase 0) {System.out.println(所有人都到达了网吧准备开始开黑);return false;} else if (phase 1) {System.out.println(大家都同意一起去次烧烤咯);return false;} else if (phase 2) {System.out.println(大家一起回寝室);return true;}return true;}}//构建一个线程任务static class DoSomeThing implements Runnable {Overridepublic void run() {/*** 向此移相器添加一个新的未到达方*/phaser.register();System.out.println(Thread.currentThread().getName() 从家里出发准备去学校后街上网开黑);phaser.arriveAndAwaitAdvance();System.out.println(Thread.currentThread().getName() 上着上着饿了说去次烧烤吗);phaser.arriveAndAwaitAdvance();System.out.println(Thread.currentThread().getName() 烧烤次完了);phaser.arriveAndAwaitAdvance();}}public static void main(String[] args) throws Exception {DoSomeThing thing new DoSomeThing();new Thread(thing, 小明).start();new Thread(thing, 小王).start();new Thread(thing, 小李).start();} } /*** 小李从家里出发准备去学校后街上网开黑* 小王从家里出发准备去学校后街上网开黑* 小明从家里出发准备去学校后街上网开黑* 所有人都到达了网吧准备开始开黑* 小李上着上着饿了说去次烧烤吗* 小明上着上着饿了说去次烧烤吗* 小王上着上着饿了说去次烧烤吗* 大家都同意一起去次烧烤咯* 小明烧烤次完了* 小李烧烤次完了* 小王烧烤次完了* 大家一起回寝室*/注意这里只是做了简单的一个使用更深入的了解我暂时也没有想要研究可以去查一查。 Exchanger Exchanger允许两个线程在某个汇合点交换对象在某些管道设计时比较有用。 Exchanger提供了一个同步点在这个同步点一对线程可以交换数据。每个线程通过exchange()方法的入口提供数据给他的伙伴线程并接收他的伙伴线程提供的数据并返回。 当两个线程通过Exchanger交换了对象这个交换对于两个线程来说都是安全的。Exchanger可以认为是 SynchronousQueue 的双向形式在运用到遗传算法和管道设计的应用中比较有用。 这个的使用我在Dubbo中的总体架构图中看到了它的身影。 十二、总结 本文主要介绍了JUC是什么、JUC框架结构概述、常用到的类汇总、相关名词、重点介绍了Lock锁Callable接口、Feature接口以及JUC三大常用工具类希望对大家有帮助欢迎评论区留言。
http://www.w-s-a.com/news/762588/

相关文章:

  • 韩雪个人网站wordpress 怎么添加网站备案信息
  • 个人网站可以做地方技能培训班
  • 品牌营销策略研究无锡 网站 seo 优化
  • 在线推广网站的方法有哪些织梦网站首页目录在哪
  • 做爰全过程免费网站的视频做网站的几个步骤
  • cpa建站教程青海西宁制作网站企业
  • 简易的在线数据库网站模板网站多服务器建设
  • 成都seo网站建设花店网页模板html
  • 义乌市网站制作网络营销策略名词解释
  • 高端品牌网站建设图片wordpress 资源站主题
  • 上海建设工程监督总站网站电商网站wordpress
  • 网站建设 医院菏泽网站建设熊掌号
  • 成都网站建设企业预约网免费建站流程
  • 网站建设胶州中国政务网站建设绩效评估
  • 合肥知名网站推广胶东国际机场建设有限公司网站
  • asp.ney旅游信息网站下载 简洁濮阳微信网站开发
  • 建设网站专业怎么上传网站程序到空间
  • 县城乡建设局网站微商城小程序哪个好
  • 博物馆门户网站建设优势重庆seo排名系统运营
  • 哪有app制作公司上海seo排名
  • 长沙建站seo公司北京招聘信息
  • 建设网站情况说明范文四川个人证书查询网官网
  • 推广学校网站怎么做公司可以做多个网站吗
  • 游戏网站后台建设郑州定制网站
  • 商务公司网站建设网站建设如何自学
  • 现在建网站可以拖拉式的吗中国国内最新新闻
  • phpstorm网站开发产品logo设计
  • 电子商务网站建设与运营什么是单页面网站
  • 西安优化网站公司南阳微信网站
  • 购物网站线下推广方案佛山快速建站哪家服务专业