java 多线程 新特性_java-多线程新特性

Java定时器相关Timer和TimerTask类

每个Timer对象相对应的是单个后台线程,用于顺序地执行所有计时器任务TimerTask对象。

Timer有两种执行任务的模式,最常用的是schedule,它可以以两种方式执行任务:1:在某个时间(Data),2:在某个固定的时间之后(long delay),都可以指定任务执行的固定延迟(long period)。

另一种是scheduleAtFixedRate,它可以在1:在某个时间(Data),2:在某个固定的时间之后(long delay),以固定的频率(long period)执行任务。

在固定延迟执行中,根据前一次执行的实际执行时间来安排每次执行。如果由于任何原因(如垃圾回收或其他后台活动)而延迟了某次执行,则后续执行也将被延迟。

在固定速率执行中,相对于已安排的初始执行时间来安排每次执行。如果由于任何原因(如垃圾回收或其他后台活动)而延迟了某次执行,则将快速连续地出现两次或更多次执行,从而使后续执行能够赶上来。

区别在于,如果指定开始执行的时间在当前系统运行时间之前,scheduleAtFixedRate会把已经过去的时间也作为周期执行,而schedule不会把过去的时间算上。

TimerTask为抽象类,由子类覆写run方法实现计时器任务要执行的操作。

实例:

1 import java.util.*;2 public classTimerTest {3

4 int count =0;5

6 public static voidmain(String[] args){7

8 new Timer().schedule(new TimerTest().new MyTimerTask(), 2000);9

10 while(true){11 //System.out.println(new Date().getSeconds());

12 System.out.println(newGregorianCalendar().get(Calendar.SECOND));13 try{14 Thread.sleep(1000);15 }catch(InterruptedException e){16 e.printStackTrace();17 }18 }19 }20 class MyTimerTask extendsTimerTask{21

22 public voidrun(){23 count =(count+1)%2;24 System.out.println("Attention!bomb!");25 new Timer().schedule(new MyTimerTask(), 2000+2000*count);26 }27 }28 }

线程范围内共享变量——ThreadLocal

API描述:ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

ThreadLocal用来隔离线程间的变量访问和修改。

Java提供的synchronized关键字使用了“同步锁”的机制来阻止线程的竞争访问,即“以时间换空间”。ThreadLocal则使用了“拷贝副本”的方式,人人有份,你用你的,我用我的,大家互不影响,是“以空间换时间”。每个线程修改变量时,实际上修改的是变量的副本,不怕影响到其它线程。

原理:将该ThreadLocal实例作为key,要保持的对象的引用作为value,通过ThreadLocal.set()设置到当前线程的ThreadLocalMap中,执行 ThreadLocal.get()时,各线程从ThreadLocalMap中取出放进去的对象,因此取出来的是各自自己线程中的对象。

1 import java.util.*;2 public class ThreadLocalDemo implementsRunnable {3 //创建线程局部变量studentLocal

4 private final static ThreadLocal studentLocal = new ThreadLocal();5

6 public static voidmain(String[] agrs) {7

8 ThreadLocalDemo td = newThreadLocalDemo();9 newThread(td).start();10 newThread(td).start();11 }12

13 public voidrun() {14

15 System.out.println(Thread.currentThread().getName() + " is running!");16 int age = new Random().nextInt(100);17

18 System.out.println(Thread.currentThread().getName() + " set age to:" +age);19

20 Student student =getStudent();21 student.setAge(age);22 System.out.println(Thread.currentThread().getName() + " first read age is:" +student.getAge());23

24 try{25 Thread.sleep(2000);26 }27 catch(InterruptedException ex) {28 ex.printStackTrace();29 }30 System.out.println(Thread.currentThread().getName() + " second read age is:" +student.getAge());31

32 }33

34 protectedStudent getStudent() {35 //获取本地线程变量并强制转换为Student类型

36 Student student =(Student) studentLocal.get();37 //线程首次执行此方法的时候,studentLocal.get()肯定为null

38 if (student == null) {39 //创建一个Student对象,并保存到本地线程变量studentLocal中

40 student = newStudent();41 studentLocal.set(student);42 }43 returnstudent;44 }45 }46 //被多线程操纵的javabean

47 public classStudent {48 private int age = 0;49

50 public intgetAge() {51 return this.age;52 }53

54 public void setAge(intage) {55 this.age =age;56 }57 }58 ---------- 运行 ----------

59 Thread-0 is running!

60 Thread-1 is running!

61 Thread-1 set age to:57

62 Thread-1 first read age is:57

63 Thread-0 set age to:44

64 Thread-0 first read age is:44

65 Thread-1 second read age is:57

66 Thread-0 second read age is:44

67

68 输出完成 (耗时 2 秒) - 正常终止

线程池

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。作用就是限制系统中执行线程的数量。线程池根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。

合理利用线程池能够带来三个好处:

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

例如:ExecutorService pool = Executors.newSingleThreadExecutor();

newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

例如:ExecutorService pool = Executors.newFixedThreadPool(2);

newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

例如:ExecutorService pool = Executors.newCachedThreadPool();

newScheduledThreadPool

创建一个延迟连接的线程池。此线程池可安排在给定延迟后运行命令或者定期地执行。

例如:

ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);

pool.scheduleAtFixedRate(new Runnable() {

public void run() {

System.out.println(System.currentTimeMillis());

}}

, 1000

, 2000

, TimeUnit.MILLISECONDS);

Callable和Future接口

Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。

Callable和Runnable有几点不同:

Callable规定的方法是call(),而Runnable规定的方法是run()。

Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。

call()方法可抛出异常,而run()方法是不能抛出异常的。

运行Callable任务可拿到一个Future对象 。

Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况isCancelled和isDone,可使用cancel方法取消任务的执行,还可使用get方法获取任务执行的结果。

1 import java.util.concurrent.*;2 import java.util.*;3 public classTest {4

5 public static void main(String[] args) throwsException{6

7 int taskSize = 5;8 //创建一个线程池

9 ExecutorService pool =Executors.newFixedThreadPool(taskSize);10 //创建多个有返回值的任务

11 List list = new ArrayList();12 for (int i = 0; i < taskSize; i++) {13 Callable c = newMyCallable(i);14 //执行任务并获取Future对象

15 Future f =pool.submit(c);16 list.add(f);17 }18 //关闭线程池

19 pool.shutdown();20

21 //获取所有并发任务的运行结果

22 for(Future f : list) {23 //从Future对象上获取任务的返回值,并输出到控制台

24 System.out.println(f.get());25 }26 }27 }28

29 class MyCallable implements Callable{30 private inttaskNum;31

32 MyCallable(inttaskNum) {33 this.taskNum =taskNum;34 }35

36 public Object call() throwsException {37 System.out.println(">>>" + taskNum + "任务启动");38 long start =System.currentTimeMillis();39

40 Thread.sleep(1000);41

42 long end =System.currentTimeMillis();43

44 System.out.println(">>>>>>" + taskNum + "任务终止");45 return taskNum + "任务返回运行结果,耗时【" + (end - start) + "毫秒】";46 }47 }48

49 ---------- 运行 ----------

50 >>>0任务启动51 >>>2任务启动52 >>>1任务启动53 >>>3任务启动54 >>>4任务启动55 >>>>>>0任务终止56 0任务返回运行结果,耗时【1001毫秒】57 >>>>>>2任务终止58 >>>>>>3任务终止59 >>>>>>1任务终止60 1任务返回运行结果,耗时【1001毫秒】61 2任务返回运行结果,耗时【1001毫秒】62 3任务返回运行结果,耗时【1001毫秒】63 >>>>>>4任务终止64 4任务返回运行结果,耗时【1001毫秒】65

66 输出完成 (耗时 1 秒) - 正常终止

读写锁

分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥。

1 importjava.util.Random;2 import java.util.concurrent.locks.*;3

4 public classReadWriteLockTest {5

6 public static voidmain(String[] args) {7 final TheData myData=newTheData();8 for(int i=0;i<3;i++){//分别开启3个线程操作读写

9 new Thread(newRunnable(){10 public voidrun() {11 while(true){12 myData.get();13 }14 }15 }).start();16

17 new Thread(newRunnable(){18 public voidrun() {19 while(true){20 myData.put(new Random().nextInt(1000));21 }22 }23 }).start();24 }25 }26 }27 classTheData{28 private Object data=null;29 private ReadWriteLock rwl=newReentrantReadWriteLock();30 public voidget(){31 rwl.readLock().lock(); //读锁开启,读线程均可进入

32 try{33 System.out.println(Thread.currentThread().getName()+"准备读取---------");34 Thread.sleep(1000);35 System.out.println(Thread.currentThread().getName()+"已经取到---------"+data);36 } catch(InterruptedException e) {37 e.printStackTrace();38 } finally{39 rwl.readLock().unlock();40 }41 }42

43 public voidput(Object data){44 rwl.writeLock().lock(); //写锁开启,这时只有一个写线程进入

45 try{46 System.out.println(Thread.currentThread().getName()+"准备写入>>>>>>>>>");47 Thread.sleep(1000);48 this.data=data;49 System.out.println(Thread.currentThread().getName()+"已经写入>>>>>>>>>"+data);50 } catch(InterruptedException e) {51 e.printStackTrace();52 } finally{53 rwl.writeLock().unlock();54 }55 }56 }57

58 ---------- 运行 ----------

59 Thread-0准备读取---------

60 Thread-2准备读取---------

61 Thread-0已经取到---------null

62 Thread-2已经取到---------null

63 Thread-1准备写入>>>>>>>>>

64 Thread-1已经写入>>>>>>>>>796

65 Thread-3准备写入>>>>>>>>>

66 Thread-3已经写入>>>>>>>>>237

67 Thread-3准备写入>>>>>>>>>

68 Thread-3已经写入>>>>>>>>>353

69 Thread-5准备写入>>>>>>>>>

70 Thread-5已经写入>>>>>>>>>646

71 Thread-5准备写入>>>>>>>>>

72 Thread-5已经写入>>>>>>>>>84

73 Thread-4准备读取---------

74 Thread-0准备读取---------

75 Thread-2准备读取---------

76 Thread-0已经取到---------84

77 Thread-2已经取到---------84

78 Thread-4已经取到---------84

79 Thread-1准备写入>>>>>>>>>

80 Thread-1已经写入>>>>>>>>>898

81 Thread-1准备写入>>>>>>>>>

82

83 输出完成 (耗时 9 秒) - 已被用户取消。

Semaphore

使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。

1 import java.util.concurrent.*;2 public classSemaphoreTest {3 public static voidmain(String[] args) {4 ExecutorService service=Executors.newCachedThreadPool();5 final Semaphore sp = new Semaphore(5);6 for(int i=0;i<10;i++){7 Runnable r = newRunnable(){8 public voidrun(){9 try{10 sp.acquire();11 } catch(InterruptedException e1) {12 e1.printStackTrace();13 }14 System.out.println("线程" + Thread.currentThread().getName() +

15 "进入,当前已有" + (5-sp.availablePermits()) + "个并发");16 try{17 Thread.sleep(2000);18 } catch(InterruptedException e) {19 e.printStackTrace();20 }21 System.out.println("线程" + Thread.currentThread().getName() +

22 "即将离开");23 sp.release();24 //下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元

25 System.out.println("线程" + Thread.currentThread().getName() +

26 "已离开,当前已有" + (5-sp.availablePermits()) + "个并发");27 }28 };29 service.execute(r);30 }31 service.shutdown();32 }33 }34 ---------- 运行 ----------

35 线程pool-1-thread-1进入,当前已有1个并发36 线程pool-1-thread-4进入,当前已有3个并发37 线程pool-1-thread-2进入,当前已有2个并发38 线程pool-1-thread-3进入,当前已有4个并发39 线程pool-1-thread-6进入,当前已有5个并发40 线程pool-1-thread-4即将离开41 线程pool-1-thread-8进入,当前已有5个并发42 线程pool-1-thread-4已离开,当前已有5个并发43 线程pool-1-thread-2即将离开44 线程pool-1-thread-10进入,当前已有5个并发45 线程pool-1-thread-2已离开,当前已有5个并发46 线程pool-1-thread-6即将离开47 线程pool-1-thread-6已离开,当前已有4个并发48 线程pool-1-thread-3即将离开49 线程pool-1-thread-3已离开,当前已有3个并发50 线程pool-1-thread-5进入,当前已有4个并发51 线程pool-1-thread-1即将离开52 线程pool-1-thread-1已离开,当前已有3个并发53 线程pool-1-thread-7进入,当前已有4个并发54 线程pool-1-thread-9进入,当前已有5个并发55 线程pool-1-thread-5即将离开56 线程pool-1-thread-10即将离开57 线程pool-1-thread-10已离开,当前已有4个并发58 线程pool-1-thread-8即将离开59 线程pool-1-thread-8已离开,当前已有3个并发60 线程pool-1-thread-5已离开,当前已有2个并发61 线程pool-1-thread-7即将离开62 线程pool-1-thread-7已离开,当前已有1个并发63 线程pool-1-thread-9即将离开64 线程pool-1-thread-9已离开,当前已有0个并发65

66 输出完成 (耗时 4 秒) - 正常终止

同步工具类

CyclicBarrier

一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。因为该barrier 在释放等待线程后可以重用,所以称它为循环的barrier。比如一个大型的任务,常常需要分配好多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候就可以选择CyclicBarrier了。

int

在所有参与者都已经在此

barrier 上调用 await 方法之前,将一直等待。

int

TimeUnit unit)

在所有参与者都已经在此屏障上调用

await 方法之前将一直等待,或者超出了指定的等待时间。

1 import java.util.concurrent.*;2

3 public classCyclicBarrierTest {4

5 public static voidmain(String[] args) {6 ExecutorService service =Executors.newCachedThreadPool();7 final CyclicBarrier cb = new CyclicBarrier(3);8 for(int i=0;i<3;i++){9 Runnable runnable = newRunnable(){10 public voidrun(){11 try{12 Thread.sleep((long)(Math.random()*10000));13 System.out.println("线程" + Thread.currentThread().getName() +

14 "即将到达集合地点1,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));15 cb.await();16

17 Thread.sleep((long)(Math.random()*10000));18 System.out.println("线程" + Thread.currentThread().getName() +

19 "即将到达集合地点2,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));20 cb.await();21 Thread.sleep((long)(Math.random()*10000));22 System.out.println("线程" + Thread.currentThread().getName() +

23 "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));24 cb.await();25 } catch(Exception e) {26 e.printStackTrace();27 }28 }29 };30 service.execute(runnable);31 }32 service.shutdown();33 }34 }35 ---------- 运行 ----------

36 线程pool-1-thread-1即将到达集合地点1,当前已有1个已经到达,正在等候37 线程pool-1-thread-2即将到达集合地点1,当前已有2个已经到达,正在等候38 线程pool-1-thread-3即将到达集合地点1,当前已有3个已经到达,都到齐了,继续走啊39 线程pool-1-thread-1即将到达集合地点2,当前已有1个已经到达,正在等候40 线程pool-1-thread-2即将到达集合地点2,当前已有2个已经到达,正在等候41 线程pool-1-thread-3即将到达集合地点2,当前已有3个已经到达,都到齐了,继续走啊42 线程pool-1-thread-2即将到达集合地点3,当前已有1个已经到达,正在等候43 线程pool-1-thread-3即将到达集合地点3,当前已有2个已经到达,正在等候44 线程pool-1-thread-1即将到达集合地点3,当前已有3个已经到达,都到齐了,继续走啊45

46 输出完成 (耗时 17 秒) - 正常终止

CountDownLatch

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

方法摘要

void

使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。

boolean

TimeUnit unit)

使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。

void

递减锁存器的计数,如果计数到达零,则释放所有等待的线程。

long

返回当前计数。

1 importjava.util.concurrent.CountDownLatch;2 importjava.util.concurrent.CyclicBarrier;3 importjava.util.concurrent.ExecutorService;4 importjava.util.concurrent.Executors;5

6 public classCountdownLatchTest {7

8 public static voidmain(String[] args) {9 ExecutorService service =Executors.newCachedThreadPool();10 final CountDownLatch cdOrder = new CountDownLatch(1);11 final CountDownLatch cdAnswer = new CountDownLatch(3);12 for(int i=0;i<3;i++){13 Runnable runnable = newRunnable(){14 public voidrun(){15 try{16 System.out.println("线程" + Thread.currentThread().getName() +

17 "正准备接受命令");18 cdOrder.await();19 System.out.println("线程" + Thread.currentThread().getName() +

20 "已接受命令");21 Thread.sleep((long)(Math.random()*10000));22 System.out.println("线程" + Thread.currentThread().getName() +

23 "回应命令处理结果");24 cdAnswer.countDown();25 } catch(Exception e) {26 e.printStackTrace();27 }28 }29 };30 service.execute(runnable);31 }32 try{33 Thread.sleep((long)(Math.random()*10000));34

35 System.out.println("线程" + Thread.currentThread().getName() +

36 "即将发布命令");37 cdOrder.countDown();38 System.out.println("线程" + Thread.currentThread().getName() +

39 "已发送命令,正在等待结果");40 cdAnswer.await();41 System.out.println("线程" + Thread.currentThread().getName() +

42 "已收到所有响应结果");43 } catch(Exception e) {44 e.printStackTrace();45 }46 service.shutdown();47

48 }49 }50 ---------- 运行 ----------

51 线程pool-1-thread-1正准备接受命令52 线程pool-1-thread-3正准备接受命令53 线程pool-1-thread-2正准备接受命令54 线程main即将发布命令55 线程pool-1-thread-1已接受命令56 线程pool-1-thread-3已接受命令57 线程main已发送命令,正在等待结果58 线程pool-1-thread-2已接受命令59 线程pool-1-thread-1回应命令处理结果60 线程pool-1-thread-2回应命令处理结果61 线程pool-1-thread-3回应命令处理结果62 线程main已收到所有响应结果63

64 输出完成 (耗时 10 秒) - 正常终止

Exchanger

提供了一个可以在对中对元素进行配对和交换的线程的同步点。每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象。

1 importjava.util.concurrent.Exchanger;2 importjava.util.concurrent.ExecutorService;3 importjava.util.concurrent.Executors;4

5 public classExchangerTest {6

7 public static voidmain(String[] args) {8 ExecutorService service =Executors.newCachedThreadPool();9 final Exchanger exchanger = newExchanger();10 service.execute(newRunnable(){11 public voidrun() {12 try{13

14 String data1 = "111";15 System.out.println("线程" + Thread.currentThread().getName() +

16 "正在把数据" + data1 +"换出去");17 Thread.sleep((long)(Math.random()*10000));18 String data2 =(String)exchanger.exchange(data1);19 System.out.println("线程" + Thread.currentThread().getName() +

20 "换回的数据为" +data2);21 }catch(Exception e){22

23 }24 }25 });26 service.execute(newRunnable(){27 public voidrun() {28 try{29

30 String data1 = "222";31 System.out.println("线程" + Thread.currentThread().getName() +

32 "正在把数据" + data1 +"换出去");33 Thread.sleep((long)(Math.random()*10000));34 String data2 =(String)exchanger.exchange(data1);35 System.out.println("线程" + Thread.currentThread().getName() +

36 "换回的数据为" +data2);37 }catch(Exception e){38

39 }40 }41 });42 service.shutdown();43 }44 }45 ---------- 运行 ----------

46 线程pool-1-thread-1正在把数据111换出去47 线程pool-1-thread-2正在把数据222换出去48 线程pool-1-thread-2换回的数据为11149 线程pool-1-thread-1换回的数据为22250

51 输出完成 (耗时 5 秒) - 正常终止

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值