读写锁、线程池demo和概念


读写锁、线程池demo和概念


读写锁、线程池都是日常中及其常用的对象,大家需要掌握。

读写锁

ReadWriteLock 维护两个锁,一个读,一个写,读锁是并发的,写锁是独占的

/*
 * 1. ReadWriteLock : 读写锁
 * 
 * 写写/读写 需要“互斥”
 * 读读 不需要互斥
 * 
 */

public class TestReadWriteLock {

 public static void main(String[] args) {
  ReadWriteLockDemo rw = new ReadWriteLockDemo();
  
  new Thread(new Runnable() {
   
   @Override
   public void run() {
    rw.set((int)(Math.random() * 101));
   }
  }, "Write:").start();
  
  
  for (int i = 0; i < 100; i++) {
   new Thread(new Runnable() {
    
    @Override
    public void run() {
     rw.get();
    }
   }).start();
  }
 }
 
}

class ReadWriteLockDemo{
 
 private int number = 0;
 
 private ReadWriteLock lock = new ReentrantReadWriteLock();
 
 //读
 public void get(){
  lock.readLock().lock(); //上锁
  
  try{
   System.out.println(Thread.currentThread().getName() + " : " + number);
  }finally{
   lock.readLock().unlock(); //释放锁
  }
 }
 
 //写
 public void set(int number){
  lock.writeLock().lock();
  
  try{
   System.out.println(Thread.currentThread().getName());
   this.number = number;
  }finally{
   lock.writeLock().unlock();
  }
 }
}

输出

Thread-1 : 0
Thread-2 : 0
Thread-7 : 0
Thread-6 : 0
Thread-9 : 0
Thread-10 : 0
Write:
Thread-0 : 31
Thread-3 : 31
Thread-4 : 31
Thread-13 : 31
Thread-14 : 31
Thread-18 : 31
Thread-21 : 31
Thread-25 : 31
Thread-29 : 31
Thread-33 : 31
Thread-34 : 31
Thread-42 : 31
Thread-37 : 31
Thread-45 : 31
Thread-49 : 31
Thread-50 : 31
Thread-53 : 31
Thread-57 : 31
Thread-58 : 31
Thread-61 : 31
Thread-62 : 31
Thread-65 : 31
Thread-66 : 31
Thread-69 : 31
Thread-73 : 31
Thread-74 : 31
Thread-77 : 31
Thread-78 : 31
Thread-82 : 31
Thread-86 : 31
Thread-89 : 31
Thread-90 : 31
Thread-94 : 31
Thread-98 : 31
Thread-8 :  31
Thread-11 : 31
Thread-15 : 31
Thread-5 :  31
Thread-16 : 31
Thread-85 : 31
Thread-70 : 31
Thread-97 : 31
Thread-54 : 31
Thread-46 : 31
Thread-93 : 31
Thread-12 : 31
Thread-30 : 31
Thread-38 : 31
Thread-22 : 31
Thread-96 : 31
Thread-41 : 31
Thread-91 : 31
Thread-81 : 31
Thread-83 : 31
Thread-92 : 31
Thread-17 : 31
Thread-76 : 31
Thread-67 : 31
Thread-87 : 31
Thread-19 : 31
Thread-95 : 31
Thread-99 : 31
Thread-26 : 31
Thread-23 : 31
Thread-71 : 31
Thread-24 : 31
Thread-20 : 31
Thread-27 : 31
Thread-28 : 31
Thread-31 : 31
Thread-32 : 31
Thread-36 : 31
Thread-35 : 31
Thread-40 : 31
Thread-39 : 31
Thread-47 : 31
Thread-44 : 31
Thread-43 : 31
Thread-48 : 31
Thread-51 : 31
Thread-52 : 31
Thread-55 : 31
Thread-56 : 31
Thread-68 : 31
Thread-59 : 31
Thread-72 : 31
Thread-60 : 31
Thread-84 : 31
Thread-63 : 31
Thread-64 : 31
Thread-88 : 31
Thread-79 : 31
Thread-75 : 31
Thread-80 : 31

线程池

一般我们测试的时候经常会新建多个线程来进行并发测试,这个时候我们是使用的单个的线程对象。

public class ThreadWithOutPool {
    public static void main(String[] args) {
        ThreadWithOutPoolOb threadWithOutPoolOb = new ThreadWithOutPoolOb();
        new Thread(threadWithOutPoolOb).start();
        new Thread(threadWithOutPoolOb).start();
    }
}

class ThreadWithOutPoolOb implements Runnable{
    private int i =0;
    @Override
    public void run() {
        while (i<=100) {
            System.out.println(Thread.currentThread().getName() + "t" +i++);
        }
    }
}
读写锁、线程池demo和概念
image-20211124153507467

实际中,如果这么去编写代码,那么代码的效率会很低,而且会耗费资源。

读写锁、线程池demo和概念
image-20211124153517933

之所以效率低,耗费资源,是因为每次线程使用时都需要创建线程,线程使用完成之后还需要销毁线程,因此,线程池就是来解决这个问题的。

线程池中会初始化一定数量的线程,在需要线程时,不需要创建,直接从池中拿出来用即可,用完了也不需要销毁,释放回到线程池中即可,因此极大地提升了效率和减小了开销。

核心接口Executor

读写锁、线程池demo和概念
image-20211124155530524

线程池的体系结构

 *  java.util.concurrent.Executor : 负责线程的使用与调度的根接口
 *   |--**ExecutorService 子接口: 线程池的主要接口
 *    |--ThreadPoolExecutor 线程池的实现类
 *    |--ScheduledExecutorService 子接口:负责线程的调度
 *     |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
 * 
 * 工具类 : Executors 
 * ExecutorService newFixedThreadPool() : 创建固定大小的线程池
 * ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
 * ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程
 * 
 * ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。

Runable

public class ThreadWithOutPool {
    public static void main(String[] args) {
        ThreadWithOutPoolOb threadWithOutPoolOb = new ThreadWithOutPoolOb();
//        new Thread(threadWithOutPoolOb).start();
//        new Thread(threadWithOutPoolOb).start();
        //1. 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
  //2. 为线程池中的线程分配任务
  for (int i = 0; i < 10; i++) {
   pool.submit(threadWithOutPoolOb);
  }

  //3. 关闭线程池
  pool.shutdown();
    }
}

Callable

这里是拿了一个实际中的实例,在某一个系统中,需要对数据提供方提供的数据的规范性、准确性进行治理,我们这边定制了一套治理的程序,原本是按照一定规则将数据分区进行治理的处理,但是随着数据量的积累,治理的效率越来越低,因此考虑加大同时处理的线程数量,这里就需要对单任务的数据再次分割,实现多线程同时治理的效果,以提升效率,这里就是用到了线程池。

定义一个HandleCallable对象,用来封装处理逻辑,该对象实现了callable接口,可以分配给单个线程进行处理
public class HandleCallable<Eimplements Callable<List<String>> {
    private static Logger logger = LoggerFactory.getLogger(HandleCallable.class);
    // 线程名称 
    private String threadName = "";
    // 需要处理的数据
    private List<E> data;

    public HandleCallable(String threadName, List<E> data) {
        this.threadName = threadName;
        this.data = data;
    }

    @Override
    public List<String> call() throws Exception {
        // 该线程中所有数据处理返回结果
        List<String> resultList = new ArrayList<>();
        if (data != null && data.size() > 0) {
            logger.info("线程:{},共处理:{}个数据,开始处理......", threadName, data.size());
            // 循环处理每个数据
            for (int i = 0; i < data.size(); i++) {
                // 需要执行的数据
                E e = data.get(i);
                // 将数据执行结果加入到结果集中
                resultList.add(e.toString()+"t"+threadName);
                logger.info("线程:{},第{}个数据,处理完成", threadName, (i + 1));
            }
            logger.info("线程:{},共处理:{}个数据,处理完成......", threadName, data.size());
        }
        return resultList;
    }

}

这里示例只是做了简单的字符串拼装和结果集返回,实际要比这个复杂的多,这里只是模拟。

刚才的测试类改为

public class ThreadWithOutPool {
    public static void main(String[] args) {
        ThreadWithOutPoolOb threadWithOutPoolOb = new ThreadWithOutPoolOb();
//        new Thread(threadWithOutPoolOb).start();
//        new Thread(threadWithOutPoolOb).start();
        //1. 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //模拟待处理数据
        List<String> d = Arrays.asList("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z");
        // 数据量大小
        int length = d.size();
        // 每个线程处理的数据个数
        int taskCount = length / 5;
        List<Future<List<String>>> r = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            // 每个线程任务数据list
            List<String> subData = null;
            if (i == (4)) {
                subData = d.subList(i * taskCount, length);
            } else {
                subData = d.subList(i * taskCount, (i + 1) * taskCount);
            }
            HandleCallable<String> handleCallable = new HandleCallable<>(Thread.currentThread().getName(),d);
            // 将线程加入到线程池
            Future<List<String>> f = pool.submit(handleCallable);
            r.add(f);
        }
//  //2. 为线程池中的线程分配任务
//  for (int i = 0; i < 10; i++) {
//   pool.submit(threadWithOutPoolOb);
//  }
//
  //3. 关闭线程池
  pool.shutdown();
        
        r.stream().forEach(x->{
            try {
                x.get().stream().forEach(System.out::println);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
    }
}

线程调度

基于工具类进行线程的延迟调度。

public class TestScheduledTestDemo {
    public static void main(String[] args) throws Exception {
        //初始化调度线程池
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);

        for (int i = 0; i < 5; i++) {
            Future<Integer> result = pool.schedule(new Callable<Integer>(){

                @Override
                public Integer call() throws Exception {
                    int num = new Random().nextInt(100);//生成随机数
                    System.out.println(Thread.currentThread().getName() + " : " + num);
                    return num;
                }

            }, 1, TimeUnit.SECONDS);

            System.out.println(result.get());
        }

        pool.shutdown();
    }
}


原文始发于微信公众号(云户):读写锁、线程池demo和概念

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/25932.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!