zoukankan      html  css  js  c++  java
  • Callable接口及线程池

    线程池

    线程容器,可设定线程分配的数量上限。

    将预先创建线程对象存入池中,并重用线程池中的线程对象。

    避免频繁的的创建和销毁。

    常用的线程池接口和类

    Executor :线程池的顶级接口

    ExecutorService :线程池接口,可通过submit(Runnable task)提交任务代码。

    Executors工厂类:通过此类可获得一个线程池。

    ​ 通过newFixedThreadPool(int nThreads)获取固定数量的线程池。

    ​ 通过newCachedThreadPool()获得动态数量的线程池,如果不够 则创建新的,没有上限。

    public class TestThreadPool {
    	public static void main(String[] args) {
    		//线程池接口(引用)    --->     Executors工具类(工厂类)
    		ExecutorService es = Executors.newFixedThreadPool(4);
    		
    		Runnable task = new MyTask();
    		
    		es.submit(task); //submit()提交任务
    		es.submit(task);
    		es.submit(task);
    		es.submit(task);
    	}
    }
    //创建接口实现类
    class MyTask implements Runnable{
    	@Override
    	public void run() {
    		for (int i = 0; i < 100; i++) {
    			System.out.println(Thread.currentThread().getName() + " - " +i);
    		}
    	}
    }
    

    Callable接口

    JDK5加入,与Runnable接口类似,实现之后代表一个线程任务。

    有泛型返回值,可以声明异常。

    Future接口

    概念:

    ​ 异步接收ExecutorService.submit()所返回的状态结果,当中包含了call()的返回值。

    方法:

    ​ V get()以阻塞形式等待Future的异步处理结果(call()的返回值)

    使用

    线程池执行Callable接口,Callable返回值由Future接收,Future当中的get() 方法就可以得到异步返回值。

    可以解决并发下的一些统计工作。

    public class TestCallable {
    	public static void main(String[] args) throws InterruptedException, ExecutionException {
    		System.out.println("程序开始");
    		//创建线程池
    		ExecutorService ex = Executors.newFixedThreadPool(3);
    		//接口引用指向实现接口
    		Callable<Integer> task1 = new MyTask1();
    		Callable<Integer> task2 = new MyTask2();
    		
    		Future<Integer> f1 = ex.submit(task1);	//得到Callable线程接口的返回值
    		Future<Integer> f2 = ex.submit(task2);
    		
    		Integer result1 = f1.get(); //以阻塞形式等待Future中的异步处理结果(call的返回值)
    		Integer result2 = f2.get(); //在没有返回值以前,get无限期等待
    		
    		System.out.println(result1 + result2); //输出5050
    	}
    }
    //Mytask1类遵从Callable接口实现call()方法
    class MyTask1 implements Callable<Integer>{
    	@Override
    	public Integer call() throws Exception {
    		Thread.sleep(1000); //休眠1秒,观察Future接口get方法
    		Integer sum = 0;
    		for (int i = 1; i <= 50; i++) {
    			sum += i;
    		}
    		return sum; //返回计算的sum值
    	}
    }
    //Mytask2类遵从Callable接口实现call()方法
    class MyTask2 implements Callable<Integer>{
    	@Override
    	public Integer call() throws Exception {
    		Thread.sleep(1000);
    		Integer sum = 0;
    		for (int i = 51; i <= 100; i++) {
    			sum += i;
    		}
    		return sum; //返回计算的sum值
    	}
    }
    

    同步和异步的区别:

    同步:

    形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续。

    异步:

    形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回。二者竞争时间片,并发执行。

    Lock接口

    提供更多实用性的方法,功能更强大,性能更优越。

    常用方法:

    void lock() - 获取锁,如锁被占用,则等待。

    boolean tryLock() - 尝试获取锁(成功返回true,失败返回false,不阻塞)

    void unlock() - 释放锁

    重入锁

    ReentrantLock:Lock接口的实现类,与synchronized一样具有互斥锁功能。

    Lock locker = new ReentrantLock(); //创建重入锁对象
    locker.lock();	//开启锁
    try{
        锁住代码块
    }finally{
        //考虑可能会出现异常,释放锁必须放入finally代码块中,避免无法释放
        locker.unlock();	//释放锁
    }
    

    读写锁

    ReentrantReadWriteLock:

    • 一种支持一写多读的同步锁,读写分离,可分别分配读锁,写锁。

    • 支持多次分配读锁,使多个读操作可以并发执行。

    互斥规则:

    写 - 写:互斥,阻塞。

    读 - 写:互斥,读阻塞写,写阻塞读。

    读 - 读:不互斥,不阻塞。

    在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。

    //读写锁
    class Student {
        //创建读写锁对象
        ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
        //创建读锁
        ReentrantReadWriteLock.ReadLock readLock =rwl.readLock();
        //创建写锁
        ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
    
        private int value;  //属性
        //读方法
        public int getValue() throws InterruptedException {
            readLock.lock();    //开启读锁
            try {
                Thread.sleep(1000); //休眠1秒,观察读的时间
                return value;
            } finally {
                readLock.unlock();  //关闭读锁
            }
        }
        //写方法
        public void setValue(int value) throws InterruptedException {
            writeLock.lock();   //开启写锁
            try {
                Thread.sleep(1000);
                this.value = value;
            } finally {
                writeLock.unlock(); //关闭写锁
            }
        }
    }
    

    最后总结:多线程相关的新旧对比

  • 相关阅读:
    Java设计模式
    一个接口多个实现类的Spring注入方式
    Mybatis 中的<![CDATA[ ]]>浅析
    方法内调另一个方法(事务)的探讨
    insertSql语句中的trim标签的使用
    Mybatis-plus的两种分页插件的配置方式
    Mybatis-plus的使用
    spring的IOC,DI及案例详解
    MySQL多表关联数据同时删除
    @Validated和@Valid区别
  • 原文地址:https://www.cnblogs.com/MonkeySun/p/13336526.html
Copyright © 2011-2022 走看看