线程再相见

 

线程的第三种写法:

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

回忆:(Runnable)

Runnable封装一个异步运行的任务,可以把它想象成为一个没有参数和返回值的异步方法.

Callable与Runnable相似,但是Callable有返回值.Callable接口是一个参数化的类型,只有一个方法call().

类型参数是返回值的类型,

例如:

Callable<Integer>表示一个最终返回Integer对象的异步计算.

Future保存异步计算的结果.可以启动一个计算,将Future对象教给某个线程,然后忘掉它.

Future对象所有者在结果计算好之后就可以获得它.

Future接口具有以下的方法:

V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
//获取结果,如果没有结果可用,就阻塞直到真正得到结果超过指定的时间为止.如果不成功,第二个方法就会抛出TimeoutException异常.

boolean cancel(boolean mayInterrupt);
//尝试取消这一任务的运行.如果任务已经开始,并且mayInterrupt参数值为true,它就会被中断.如果成功执行了取消操作,返回true.

boolean isCancelled();
//如果任务在完成前被取消了,则返回true.

boolean isDone();
//如果任务结束,无论是正常结束,中途取消或发生异常,都返回true.

FutureTask包装器是一种非常便利的机制,它可以将Callable转换为Future和Runnable,它同时实现二者的接口.

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask task = new FutureTask(() -> {
            System.out.println(Thread.currentThread().getName() + "线程开始执行");
            Thread.sleep(1000);//线程睡眠一秒
            return "结束";
        });
        //创建和启动新的线程
        new Thread(task).start();
        //获取返回结果
        System.out.println(task.get());
    }

执行器<Executor>

构建一个新的线程是有一定的代价的,因为涉及与操作系统的交互,如果程序中创建了大量的生命期很短的线程,应该使用线程池(thread pool).一个线程池中包含许多准备运行的空闲线程.将Runnable对象交给线程池,就会有一个线程调用Run方法.当Run方法退出时,线程不会死亡,而是在池中准备为下一个请求提供服务.

另一个使用线程池的理由:

减少并发线程的数目.

创建大量线程会大大降低性能甚至使虚拟机崩溃.如果有一个会创建多个虚拟机的算法,应该使用一个线程数"固定的"线程池以限制并发的线程总数.

执行器<Executor>类有许多静态工厂方法用来构造线程池.

方法描述应用场景

缓冲线程池

Executors.newCachedThreadPool()

核心线程数是:0;最大线程数是Integer的最大值(救急线程可以无限创建);生存时间是60s.适合任务数比较密集,但每个任务执行时间较短的情况.

固定大小的线程池

Executors.newFixedThreadPool(x)

x为需要创建的线程数,固定的值,但是可以修改,空闲线程会一直被保留;

核心线程数==最大线程数(没有救急线程被创建)

阻塞队列  无界 可以存放任意数量的任务

适合执行数量有限,长时间运行的任务

单线程线程池

Executors.newSingleThreadExecutor()

只有一个线程的线程池,该线程顺序执行每一个提交的任务希望多个任务进行排队的时候执行

区别:

Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改

Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改

int getLargPoolSize:返回线程在该执行器生命周期中的最大尺寸.

一个核心的ExecutorService的实现类:ThreadPoolExecutor

ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue)


corePoolSize 核心线程数目 (最多保留的线程数)
5
maximumPoolSize
10

workQueue 阻塞队列 如果任务超过了核心线程数,进入队列进行排队,直到有空闲的线程
10

如果任务过多,阻塞队列都放不下了,还会创建新的线程来救急
corePoolSize+救急的线程 <= maximumPoolSize(最大线程数)
21 会抛出拒绝提交任务异常 

keepAliveTime 生存时间- 针对救急线程
60
unit 时间单位 秒

带有日程安排功能的线程池:

ScheduledExecutorService service = Executors.newScheduledThreadPool(5);

// 让任务推迟一段时间执行
// 参数1.任务对象, 参数2,3 推迟的时间
/*service.schedule(()->{
    System.out.println("执行任务...");
}, 10L, TimeUnit.SECONDS);*/

// 以一定的频率反复执行任务(任务不会重叠)
// 参数1,任务对象, 参数2,初始推迟时间, 参数3,4 时间间隔和单位
/*service.scheduleAtFixedRate(()->{
    try {
        Thread.sleep(1200);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("hello");
}, 0, 1, TimeUnit.SECONDS);*/

// delay表示从上一个任务结束,到下一个任务开始之间的时间
service.scheduleWithFixedDelay(()->{
    System.out.println("hello");
}, 0, 1, TimeUnit.SECONDS);


//        service.shutdown();

线程池执行:

newCachedThreadPool方法创建了一个线程池,对于每个任务,如果有空闲线程可用,立即让它执行任务,如果没有可用的空闲线程,则创建一个新的线程.

newFixedThreadPool方法创建了一个具有固定大小的线程池,如果提交 的任务数多于空闲的线程数,那么把得不到服务的任务方法队列中,当其他任务完成以后再运行它们.

newSingleThreadExecutor是一个退化了的大小为一的线程池,有一个线程执行提交的任务,一个接着一个.

这三个方法返回实现了executorSerevice接口的ThreadPoolExecutor类的对象.

总结在使用连接池时应该做的事情:

1.调用了Executors类中的静态方法newCachedThreadPool或者newFixedThreadPool

2,调用submit提交Runnable或者Callable对象.

3.如果想要取消一个任务,或者提交Callable对象,那就要保存好返回的future对象.

4,当不在提交任何任务时,调用shutdown,结束.

线程安全集合类:

StringBuffer 线程安全

String 不可变类 线程安全

Random 线程安全

Vectr 实现了List 并且线程安全

Hashtable 实现了map,并且线程安全

jdk1.5新增的线程安全类

ConcurrentHashMap 实现了Map,并且线程安全

ConcurrentSkipLsitMap 实现了Map(可排序),并且线程安全

CopyOnWriteArrayList实现了List, 并且线程安全

BlockingQueue阻塞队列

方法正常动作特殊情况下的动作
add添加一个元素如果队列满,则抛出一个IllegalStateException异常
element返回队列的头元素

如果队列空,抛出NoSuchElementException异常

offer添加一个元素并且返回true如果队列满,则返回false
peek返回队列的头元素如果队列空,则返回null
poll移除并返回队列的头元素如果队列空,则返回null
put添加一个元素如果队列满,则阻塞
remove移除并返回头元素如果队列空,则抛出NoSuchElementException异常
take移除并返回头元素如果队列空,则阻塞
// 线程安全的队列, 其中capacity 表示队列的元素上限
        BlockingQueue<String> queue1 = new ArrayBlockingQueue<>(3);
        //                             或 new LinkedBlockingDeque<>(3);

生产者/消费者模式

 private static BlockingQueue<chanpin> queue = new ArrayBlockingQueue<>(5);

    public static void main(String[] args) {
        //生产者线程
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                chanpin c = new chanpin(i);
                System.out.println(Thread.currentThread().getName()+"生产了" + c);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    queue.put(c);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        //消费者线程

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                for (int j = 0; j < 3; j++) {
                    try {
                        Thread.sleep(1000);
                        chanpin c = queue.take();
                        System.out.println(Thread.currentThread().getName()+"消费了" + c);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

}


class chanpin {
    private int i;

    public chanpin() {
    }

    public chanpin(int i) {
        this.i = i;
    }

    @Override
    public String toString() {
        return "chanpin{" +
                "i=" + i +
                '}';
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值