JDK5中的多线程并发库.docx

上传人:b****5 文档编号:27866461 上传时间:2023-07-05 格式:DOCX 页数:10 大小:41.07KB
下载 相关 举报
JDK5中的多线程并发库.docx_第1页
第1页 / 共10页
JDK5中的多线程并发库.docx_第2页
第2页 / 共10页
JDK5中的多线程并发库.docx_第3页
第3页 / 共10页
JDK5中的多线程并发库.docx_第4页
第4页 / 共10页
JDK5中的多线程并发库.docx_第5页
第5页 / 共10页
点击查看更多>>
下载资源
资源描述

JDK5中的多线程并发库.docx

《JDK5中的多线程并发库.docx》由会员分享,可在线阅读,更多相关《JDK5中的多线程并发库.docx(10页珍藏版)》请在冰豆网上搜索。

JDK5中的多线程并发库.docx

JDK5中的多线程并发库

1.线程

Ø线程与进程的关系

进程是一个正在执行中的程序,线程是程序执行过程中的一条分支,一个进程可以有多个线程。

Ø创建线程的方式

•Thread子类

自定义一个类继承Thread类,重写其中run()方法。

创建该类对象并调用start()方法,就会开启一条新线程并执行run()方法中的代码。

•Runnable

自定义一个类实现Runnable接口,重启其中run()方法。

创建该类对象并创建Thread对象,在创建Thread对象时将Runnbale对象传入构造函数。

调用Thread对象的start()方法时就会开启线程运行Runnbale对象的run()方法中的代码。

ØThread类常用方法

•sleep(long)

当前线程休眠指定毫秒

•currentThread()

获取当前线程对象

•getName()

获取当前线程的名字

•setName(String)

设置当前线程的名字

•setDaemon(boolean)

设置线程为守护线程,守护线程在进程中没有任何非守护线程执行时自动结束,该方法只能在线程调用start()之前使用

•join()

当前线程暂停,等待加入的线程运行结束之后继续

2.计时器

Ø什么是计时器

Timer是一种工具,用其安排在后台线程中执行的任务。

可安排任务执行一次,或者定期重复执行。

可以通过构造函数设置其线程名及是否为守护线程。

Ø安排任务

•schedule(TimerTask,long)

安排任务,延迟指定毫秒后执行

•schedule(TimerTask,Date)

安排任务,在指定时间执行

•schedule(TimerTask,long,long)

安排任务,延迟指定毫秒后执行第一次,之后每间隔指定毫秒重复运行

•schedule(TimerTask,Date,long)

安排任务,在指定时间时执行第一次,之后每间隔指定毫秒重复运行

Ø练习

使用计时器安排任务,先2秒执行一次,然后4秒执行一次,再2秒执行一次,4秒执行一次,循环……

使用计时器安排任务,周一到周五每天凌晨4点执行。

3.同步

Ø同步代码块

使用synchronized(锁对象){同步代码}形式进行同步,多个线程执行同步代码块时如果使用的锁对象相同,只能有一个线程执行。

Ø同步方法

使用synchronized关键字修饰方法,这时整个方法都是同步的,使用this作为锁对象。

Ø静态同步方法

静态方法也可以使用synchronized关键字修饰,方法内部的代码也是同步的,这时的锁对象是当前类的Class对象。

4.通信

Ø等待

在同步代码中调用锁对象的wait()方法,可以让当前线程等待

Ø通知唤醒

使用锁对象的notify()方法可以唤醒在该对象上等待的随机一个线程

使用锁对象的notifyAll()方法可以唤醒在该对象上等待的所有线程

Ø练习

创建2个线程,其中一个线程内部执行3次打印,另外一个线程内部执行5次打印,第一个线程再执行3次,第二个执行5次,如此交替执行10次。

5.线程范围内共享数据

Ø应用场景

在程序开发过程中我们经常需要在同一个线程中共享数据,例如我们常见的银行转账的案例,转入和转出是同一个线程上执行的两个方法,他们应该共享一个事务对象。

Ø解决方案

•自定义Map

使用一个Key对象为Thread类型的Map用来保存数据。

在存储对象时将当前线程存为Key,数据存为Value。

获取对象时使用当前线程对象即可获取到线程内部共享的数据。

•ThreadLocal

Java中为我们提供了一个和当前线程相关的容器ThreadLocal。

当调用其set()方法时会将数据和当前线程绑定,调用get()方法时则是获取和当前线程绑定的数据。

一个ThreadLocal只能存储一个数据,如果有多个数据需要在线程范围内共享,可以创建多个ThreadLocal,或者将多个数据存入一个对象,将对象存入ThreadLocal。

Ø练习

开启4个线程,线程开启后均为死循环,它们同时操作一个int变量,2个线程中对变量每次加3并且打印,另外2个线程中对变量每次减3并打印。

6.原子类型

Ø什么是原子类型

JDK5之后,Java中的java.util.concurrent.atomic包中提供了一些原子类型,可以对Integer、Long、Boolean和引用数据类型进行一些常用的操作,这些操作都是具有原子性的。

原子性即为不可分割的,例如我们说一组操作具有原子性,就是指这组操作的执行过程中CPU不会跳转到其他线程工作

Ø常用API

•AtomicInteger:

具有原子性的Integer

addAndGet(int)对数字增加指定值,并且返回更新后的值

incrementAndGet()对数字加1并且返回更新后的值

decrementAndGet()对数字减1并且返回更新后的值

set(int)设置为指定数值

•AtomicIntegerArray:

具有原子性的Integer数组

addAndGet(int,int)对指定索引位置上的数值增加指定值,并且返回更新后的值

incrementAndGet(int)将指定索引上的数值加1并返回

decrementAndGet(int)将指定索引上的数值减1并返回

set(int,int)将指定索引上的值赋值为指定值

•AtomicIntegerFieldUpdater:

Integer字段更新器

newUpdater(Class,String)获取指定类的指定属性的更新器

addAndGet(Object,int)将指定对象上的属性设置为指定值

7.线程池

Ø什么是线程池

当我们需要执行多个任务,每个任务都需要一个线程去执行的时候,并不一定需要每次都创建一个线程,因为线程的创建和销毁都是比较消耗性能的。

我们可以事先创建一些线程,存储在一个容器池中,当需要执行任务的时候从池中获取一个线程,任务执行结束之后再将线程还回池中。

JDK5之后,Java中提供了工具类Exetors,用来创建各种线程池。

ExecutorService、ScheduledExecutorService

Ø固定大小的线程池

使用newFixedThreadPool(int)方法创建一个固定大小的线程池,池内线程个数为指定int值

调用线程池的execute(Runnable)方法来添加一个任务,如果池中有空闲线程,将会立即执行任务,如果池中没有空闲线程,任务将会等待池中线程完成之前的任务之后才执行

Ø缓冲线程池

使用newCachedThreadPool()方法创建一个缓冲线程池,池内最初没有线程

调用线程池的execute(Runnable)方法来添加一个任务,如果池中有空闲线程,将会立即执行任务,如果池中没有空闲线程则会创建新线程执行,线程空闲60秒后销毁

Ø单独线程池

使用newSingleThreadExecutor()方法创建一个单独线程池,池内始终只有1个线程,如果该线程被杀死,则会重新创建

调用线程池的execute(Runnable)方法来添加一个任务,如果池中线程空闲,将会立即执行任务,如果线程忙碌,则等待线程完成上次的任务之后才执行

Ø计时器线程池

使用newScheduledThreadPool(int)方法创建一个计时器线程池,池内线程数为指定int值

调用线程池的execute(Runnable)可以添加一个立即执行的任务,原理和newFixedThreadPool(int)相同

还可以调用schedule(Runnable,long,TimeUnit)方法来指定定时任务

ØCallable和Futrue

ExecutorService还可以调用submit(Callable)方法执行一个任务,在call()方法中定义任务内容并且返回一个值

submit方法会返回一个Future对象,在任务执行结束时,Future对象的get()方法可以得到call()方法返回的值,如果调用get()方法时任务未完成,那么线程将阻塞等待任务完成

ØCompletionService

使用CompletionService可以批量添加任务之后获取最先完成的任务返回的结果

使用构造函数ExecutorCompletionService(Executor)创建对象,然后使用submit(Callable)方法添加任务

调用CompletionService的take()方法可以获取到最先完成的任务返回的Future对象

8.锁

ØReentrantLock

使用在多线程并发时可以使用ReentrantLock对象的lock()方法开始同步,unlock()方法结束同步。

使用相同Lock对象同步的代码同一时间只能一个线程执行,原理和synchronized相同。

通常解锁的代码会放在finally中执行,避免出现一场无法解锁的问题。

ØReentrantReadWriteLock

使用构造函数ReentrantReadWriteLock()可以创建读写锁对象,调用其readLock()可以获取读锁,调用writeLock()方法可以获取写锁

读锁和读锁之间不互斥,写锁和写锁之间互斥,写锁和读锁也互斥

Ø练习

设计一个缓存容器,可以缓存多个对象。

当调用缓存容器取数据时如果其中没有查询数据,则从数据库查找,如果有就直接返回。

9.条件分支

ØCondition

使用Lock的newCondition()方法可以获取一个Condition,Condition对象拥有和Object类似的wait()、notify()、notifyAll()功能,分别为await()、signal()、signalAll()

区别是如果使用synchronized同步时只能使用锁对象来wait()、notify()、notifyAll(),这时notify()方法只能唤醒随机一个线程

而使用Condition时可以创建多个分支对象,让线程在不同的分支上等待,并且可以唤醒指定分支上的线程

Ø练习

创建3个线程,其中一个线程内部执行3次打印,一个线程内部执行5次打印,另外一个线程内部执行7次.打印。

第一个线程再执行3次,第二个执行5次,第三个执行7次,如此交替执行10次。

10.同步工具类

ØSemaphore

可以在多个线程并发时指定同时执行线程的个数。

使用构造函数Semaphore(int)创建信号灯,指定并发个数。

在线程开始执行后调用acquire()方法占用一个并发数,线程结束时使用release()释放一个并发数。

类似银行排号功能,多个客户即为多条线程,并发数量取决于银行开了几个窗口,其他客户等待前面客户办理业务结束之后排队继续。

ØCyclicBarrier

可以在多线程并发执行的时设置标记,等待其他线程。

使用构造函数CyclicBarrier(int)创建对象,指定等待线程的个数。

在线程开始执行之后可以使用CyclicBarrier的await()方法控制先到的线程等待,直到等待的线程到达指定个数时所有线程继续。

类似集体旅游爬山,从山脚开始爬山每个人速度不同,到达山顶的时间不同,但是先到的等待后来的一起开饭。

饭后下山的速度也有所不同,先到的在车上等待后来的一起开车回家。

ØCountDownLatch

可以在多线程并发执行时设置等待,等待倒计时结束之后继续。

使用构造函数CountDownLatch(int)创建对象,指定倒计时次数。

在线程开始之后可以使用await()方法控制线程等待倒计时,使用countDown()方法进行倒计时,当调用countDown()到达指定次数之后await()的线程继续执行。

类似于田径赛跑,运动员等待裁判倒数开始,裁判等待运动员到达终点。

ØExchanger

可以在多线程并发时设置等待,等待另一线程运行到指定位置,并且交换数据。

使用构造函数Exchanger()创建对象。

在线程开始之后可以使用exchange(Object)方法控制当前线程等待,直到有另一个线程也调用该方法时交换数据,并继续执行。

类似于买卖双方约定交易地点,其中一方先到之后等待另外一方,双方到齐之后一手交钱一手交货。

11.阻塞队列

ØBlockingQueue

阻塞队列,可以支持多个线程向队列中添加获取元素。

ArrayBlockingQueue为数组实现的固定大小的阻塞队列

LinkedBlockingQueue为链表实现的不固定大小的阻塞队列

练习:

使用BlockingQueue完成线程之间的通信,3个线程轮流打印

12.同步集合

ConcurrentHashMap线程安全的HashMap,类似于Hashtable

ConcurrentSkipListMap线程安全的TreeMap

ConcurrentSkipListSet线程安全的TreeSet

CopyOnWriteArrayList线程安全的ArrayList

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 党团工作 > 其它

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1