※快速掌握java多线程

※快速掌握java多线程

一.基本概念

(一)程序、进程与线程

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

(二)并行与并发
并行是线程在多喝cpu上运行。
并发就是指程序同时处理多个任务的能力, 并发编程的根源在于对多任务情况下对访问资源的有 效控制。在CPU来回切换
(三)同步与异步
同步是指你做了某件事,这件事还没结束,你就必须等待完成。
异步是指你做了某件事,这件事还没结束,你可以去做别的事情先
(四)临界区
临界区用来表示一种公共资源与共享数据,可以被多 个线程使用。
同一时间只能有一个线程访问临界区(阻塞状态), 其他资源必须等待。
(五)死锁、饥饿、活锁
死锁:你在等我释放资源,我也等你释放资源,大家互不相让。
饥饿:一直无法获取足够资源的线程状态
活锁:谁都互相礼让所要的资源
(六)线程安全
在拥有共享数据的多条线程并行执行的程序中,线程 安全的代码会通过同步机制保证各个线程都可以正常 且正确的执行,不会出现数据污染等意外情况。
(七)线程安全三大特性
原子性 – 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何 因素打断,要么就都不执行。i = i + 1
可见性 – 当多个线程访问同一个变量时,一个线程修改了这个变量的值,其 他线程能够立即看得到修改的值。
有序性 – 如果在本线程内观察,所有的操作都是有序的;如果在一个线程观 察另一个线程,所有的操作都是无序的。
(八)java内存模型
在这里插入图片描述
(九)volatile重排序
(十)线程的5种状态
在这里插入图片描述

二.创建多线程(主线程+垃圾回收线程+自己创建的)

(一)继承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{
    @Override
    public 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 + "米/秒)");
        }
    }
}

(二)实现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 {
    @Override
    public 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 + "米/秒)");
        }
    }
}

(三)继承Callable接口

package thread;

import java.util.Random;
import java.util.concurrent.*;

public class Match3 {
    public static void main(String[] args) throws Exception {
   
        Runner3 liuxiang = new Runner3();//实例化Callable对象
        liuxiang.setName("刘翔");
        final Integer call = liuxiang.call();
        Runner3 laoqi = new Runner3();
        laoqi.setName("老齐");
        Runner3 op = new Runner3();
        op.setName("路飞");

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
   
    }
}

class Runner3 implements Callable<Integer>{
    private String name ;
    public void setName(String name){
        this.name = name;
    }
    //实现Callable接口可以允许我们的线程返回值或抛出异常
    @Override
    public 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;
    }
}

(四)继承Callable接口+线程池管理

package thread;

import java.util.Random;
import java.util.concurrent.*;

public class Match3 {
    public static void main(String[] args) throws Exception {
        //创建一个线程池。里面天生有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接口可以允许我们的线程返回值或抛出异常
    @Override
    public 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;
    }
}

(五)三种方式比较
在这里插入图片描述

三.线程同步(控制线程的执行顺序)

(一)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  void first(){
      synchronized (lock) { //同步代码块,在同一时间只允许有一个线程执行访问这个方法
            System.out.printf("琴");
            System.out.printf("瑟");
            System.out.printf("琵");
            System.out.printf("琶");
            System.out.println();
       }
    }
    public  void second(){
        synchronized (lock) { //因为两个同步代码指向了同一把锁lock,所以在同一个时间内只允许有一个代码块执行,其他等待
            System.out.printf("魑");
            System.out.printf("魅");
            System.out.printf("魍");
            System.out.printf("魉");
            System.out.println();
        }

    }
}

(二)synchronized方法 - this当前对象

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{
   
    public synchronized void first(){
 { 
            System.out.printf("琴");
            System.out.printf("瑟");
            System.out.printf("琵");
            System.out.printf("琶");
            System.out.println();

    }
    public synchronized void second(){
        
            System.out.printf("魑");
            System.out.printf("魅");
            System.out.printf("魍");
            System.out.printf("魉");
            System.out.println();
        }

    }
}

(三)synchronized静态方法 - 该类的字节码对象

package thread;

import java.util.Random;

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{

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

    }
}

四.死锁

(一)死锁的原因及措施
在这里插入图片描述
(二)死锁演示

package thread;

public class DeadLock {
    private static String fileA = "A文件";
    private static String fileB = "B文件";

    public static void main(String[] args) {
        new Thread(){ //线程1
            public 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(){ //线程2
            public 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();
    }
}

五.线程安全(单线程和多线程执行结果相同)

(一)
在这里插入图片描述
(二)
在这里插入图片描述
(三)解决线程不安全方法:
利用上面线程同步的方法解决

六.JDK并发工具包

(一)概述
并发是伴随着多核处理器的诞生而产生的,为了充分 利用硬件资源,诞生了多线程技术。但是多线程又存 在资源竞争的问题,引发了同步和互斥的问题,JDK 1.5推出的java.util.concurrent(并发工具包)来解决 这些问题。
(二)线程池
在java.util.concurrent中,提供了工具类Executors(调 度器)对象来创建线程池,可创建的线程池有四种:

  1. CachedThreadPool - 可缓存线程池

    package juc;
     
     import java.util.concurrent.ExecutorService;
     import java.util.concurrent.Executors;
     
     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() {
                     @Override
                     public void run() {
                         System.out.println(Thread.currentThread().getName() + ":" + index);
                     }
                 });
     
             }
             try {
                 Thread.sleep(1000); //跟线程足够的运行时间
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
     
             threadPool.shutdown();
         }
     }
    
  2. FixedThreadPool - 定长线程池

    package juc;

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    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() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + ":" + index);
                    }
                });
            }
            try {
                Thread.sleep(1000); //跟线程足够的运行时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            threadPool.shutdown();
        }
    }
    
  • SingleThreadExecutor - 单线程池
package juc;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + index);
                }
            });
        }
        try {
            Thread.sleep(1000); //跟线程足够的运行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //shutdown() 代表关闭线程池(等待所有线程完成)
        //shutdownNow() 代表立即终止线程池的运行,不等待线程,不推荐使用
        threadPool.shutdown();
    }
}
  • ScheduledThreadPool - 调度线程池

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

(三)CountDownLatch - 倒计时锁

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

    import java.util.concurrent.CountDownLatch;
     import java.util.concurrent.ExecutorService;
     import java.util.concurrent.Executors;
    
     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() {
                     @Override
                     public 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();
         }
     }
    

(四)JUC之Semaphore信号量
信号量表示同一个资源能同时被多少人使用。

package juc;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

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() {
                @Override
                public 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();
        }
    }
}

设定没有获取资源的时候提示信息

package juc;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreSample2 {
    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() {
                @Override
                public void run() {
                    try {
                        //尝试获取一次信号量,5秒钟内获取到返回true,否则返回false,也可以不设置参数,表示立刻不行就算
                        if(semaphore.tryAcquire(6, TimeUnit.SECONDS)) {
                            play();
                            semaphore.release();//执行完成后释放这个信号量,“从跑道出去”
                        }else{
                            System.out.println(Thread.currentThread().getName() + ":对不起,服务器已满,请稍后再试");
                        }
                    } 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();
        }
    }
}

(五)JUC之CyclicBarrier循环屏障

  • CyclicBarrier是一个同步工具类,它允许一组线程互相等 待,直到到达某个公共屏障点。与CountDownLatch不同的是该barrier在释放等待线程后可以重用,所以称它为循 环(Cyclic)的屏障(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() {
                 @Override
                 public 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();
         }
    
     }
    

    }

(六)JUC之ReentrantLock重入锁
重入锁是指任意线程在获取到锁之后,再次获取该锁而不会被该锁 所阻塞
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) {
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里面否则会出现死锁
}

}

(六)JUC之Condition等待与唤醒
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package juc;

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

public class ConditionSample {
    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock();
        Condition condition1 = reentrantLock.newCondition();
        Condition condition2 = reentrantLock.newCondition();
        Condition condition3 = reentrantLock.newCondition();

        new Thread(new Runnable() {
            @Override
            public void run() {
                reentrantLock.lock();
                try {
                    condition1.await();
                    Thread.sleep(1000);
                    System.out.println("身");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                finally {
                    reentrantLock.unlock();
                }

            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                reentrantLock.lock();
                try {
                    condition2.await();
                    Thread.sleep(1000);
                    System.out.println("体");
                    condition1.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                finally {
                    reentrantLock.unlock();
                }

            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                reentrantLock.lock();
                try {
                    condition3.await();
                    Thread.sleep(1000);
                    System.out.println("健");
                    condition2.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                finally {
                    reentrantLock.unlock();
                }

            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                reentrantLock.lock();
                try {

                    Thread.sleep(1000);

                    System.out.println("康");
                    condition3.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                finally {
                    reentrantLock.unlock();
                }

            }
        }).start();





    }
}

(七)JUC之Callable与Future.

package juc;

import java.util.concurrent.*;

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

    @Override
    public Boolean call() throws Exception {
        boolean isprime = true;
        for(int i = 2 ; i < num ; i++) {
            if (num % i == 0) {
                isprime = false;
                break;
            }
        }

        return isprime;
    }
}

(八)线程安全又效率高的容器

  • ArrayList -> CopyOnWriteArrayList - 写复制列表

  • 在这里插入图片描述

    package juc;

    import java.util.Iterator;
    import java.util.List;
    import java.util.concurrent.CopyOnWriteArrayList;

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

  • HashSet -> CopyOnWriteArraySet - 写复制集合

  • HashMap -> ConcurrentHashMap - 分段锁映射

在这里插入图片描述
package juc;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class ConcurrentHashMapSample {
    public static int users = 100;//同时模拟的并发访问用户数量
    public static int downTotal = 50000; //用户下载的真实总数
    public static ConcurrentHashMap count = new ConcurrentHashMap() ;//计数器

    public static void main(String[] args) {
        ExecutorService executorService  = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(users);
        for(int i = 0 ; i < downTotal ; i++){
            final Integer index = i;
            executorService.execute(()->{
                //通过多线程模拟N个用户并发访问并下载
                try {
                    semaphore.acquire();
                    count.put(index, index);
                    semaphore.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdown();//关闭调度服务
        System.out.println("下载总数:" + count.size());
    }

}

(九)原子包
Atomic包是java.util.concurrent下的另一个专门为线程安全 设计的Java包,
包含多个原子操作类。
Atomic常用类
– AtomicInteger – AtomicIntegerArray – AtomicBoolean – AtomicLong – AtomicLongArray
在这里插入图片描述
在这里插入图片描述
Atomic的应用场景 u 虽然基于CAS的线程安全机制很好很高效,但要说的 是,并非所有线程安全都可以用这样的方法来实现, 这只适合一些粒度比较小型,如计数器这样的需求用起 来才有效,否则也不会有锁的存在了。

   package juc;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerSample {
    public static int users = 100;//同时模拟的并发访问用户数量
    public static int downTotal = 50000; //用户下载的真实总数
    public static AtomicInteger count = new AtomicInteger() ;//计数器

    public static void main(String[] args) {
        ExecutorService executorService  = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(users);
        for(int i = 0 ; i < downTotal ; i++){
            executorService.execute(()->{
                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.getAndIncrement(); //count++
    }


}

七.总结

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值