Java编程思想笔记整理-多线程
实现线程的方法:
(1)继承thread(底层实现了runable)
(2)实现Runable
(3)Executor创建线程池
(4)实现Callable接口(带返回结果)
对于callable接口,可以通过FutureTask包装实现线程,也可以使用ExecutorService对象的submit实现。
使用executor创建线程池:(例)Executors.newCachedThreadPool返回一个ExecutorService对象(类似于命令模式)通过该对象的execute方法可以启动线程。
可带参数:ThreadFactory对象,批量生成线程
FixedThreadPool:分配指定数量的线程。
SingleThreadExecutor:类似于单例的fixed,顺序执行(序列化任务)
通常情况下整个系统的所有任务都由一个Executor创建和管理。调用shutdown方法可以防止新的任务被提交给该Executor,而之前提交的任务都会完成(未调用shutdown会导致线程挂起)
Callable接口:实现后任务会返回值,泛型规定返回类型(call方法中)
通过executorservice的submit方法调用callable或Runable对象,返回future对象。
(future:在未执行完时返回一个对象,可以通过isDone判断是否执行完。使用get方法来获取其中的值。但是在未执行完成前调用get方法可能会引起阻塞)
ScheduledThreadPoolExecutor:定期任务线程池,主要方法:scheduled
sleep:jdk5以后引入了TimeUnit.XX.sleep(X)的方式
守护线程创建的任何线程都是守护线程。当用户线程执行完毕之后,守护线程会自动结束(不会执行finally等,突然暴毙)
线程中的异常不会被trycatch处理,会直接抛出到控制台。解决方案:
线程设置异常处理器UncaughtExceptionHandler,实现该接口的对象作为参数。
推荐:使用ThreadFactory,内部为每一个线程附着异常处理器
全局使用一个时:调用静态方法:Thread.setDefaultUncaughtExceptionHandler设置
并发工具:
BlockingQueue(接口):内部同步,解决生产者消费者问题。为空时阻塞消费者,为满时阻塞生产者
主要方法:put take
实现类(例):DelayQueue PriorityBlockingQueue(非FIFO) ArrayblockingQueue SynchronousQueue (放和取交替) 等
DelayQueue:无界的BlockingQueue,放置实现了delayed接口的对象。该对象只有在到期时才能取走。可以理解为优先级队列的一种变体。根据到期时间的先后排序,决定取走顺序。没有到期任务时无法取出
CountDownLatch:(只触发一次)该对象初始化时可以设置计数值,通过调用该对象的countdown方法使其-1.
在此对象上调用await的所有线程都会阻塞,等待其计数值为0时全部唤醒。
CyclicBarrier(触发多次,通过reset重置计数值):设置一个栅栏动作(作为runable类型的构造参数传入,可通过匿名内部类设置),计数值达到0时首先自动执行,随后其他任务放行.(人满开车!)
通过调用await方法进行当前线程阻塞
Semaphore:类似于可以获取资源的通行证通过acquire获取通行证(表明资源被占用),通过release收回通行证(表明资源已经释放)。仅起到确保作用,释放和占用需要手动完成
当没有通行证(可用资源)时,semaphore对象将阻塞调用。
Exchanger:成对的线程交换对象(大多用于两个)
线程调用exchange方法,等待(此时阻塞)与其成对的线程调用exchange方法,成对调用后交换数据
主要方法:exchange,该方法返回值即为交换过来的值
CopyOnWriteArrayList:免锁容器,不会使用synchronized。允许并发的读写。在写入时创建底层数组的副本进行修改。完成后会将新数组换入,使其可见(注意完成后可见)
CopyOnWriteArraySet:使用CopyOnWriteArrayList实现免锁
ConcurrentHashMap和ConcurrentLinkedQueue:使用类似的技术,但是只会创建部分内容的副本
ConcurrentHashMap底层:数组,每个元素是个HashMap,分段进行数据处理
以上均不会抛出ConcurrentModificationException