知识大全 Java 5.0多线程编程
Posted 知
篇首语:如果不能改变结果,那就完善过程。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Java 5.0多线程编程相关的知识,希望对你有一定的参考价值。
Java 5.0多线程编程 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
Java自 年面世以来得到了广泛得一个运用 但是对多线程编程的支持Java很长时间一直停留在初级阶段 在Java 之前Java里的多线程编程主要是通过Thread类 Runnable接口 Object对象中的wait() notify() notifyAll()等方法和synchronized关键词来实现的 这些工具虽然能在大多数情况下解决对共享资源的管理和线程间的调度 但存在以下几个问题
过于原始 拿来就能用的功能有限 即使是要实现简单的多线程功能也需要编写大量的代码 这些工具就像汇编语言一样难以学习和使用 比这更糟糕的是稍有不慎它们还可能被错误地使用 而且这样的错误很难被发现
如果使用不当 会使程序的运行效率大大降低
为了提高开发效率 简化编程 开发人员在做项目的时候往往需要写一些共享的工具来实现一些普遍适用的功能 但因为没有规范 相同的工具会被重复地开发 造成资源浪费
因为锁定的功能是通过Synchronized来实现的 这是一种块结构 只能对代码中的一段代码进行锁定 而且锁定是单一的 如以下代码所示
synchronized(lock)
//执行对共享资源的操作
……
一些复杂的功能就很难被实现 比如说如果程序需要取得lock A和lock B来进行操作 然后需要取得lock C并且释放lock A来进行操作 Java 之前的多线程框架就显得无能为力了
因为这些问题 程序员对旧的框架一直颇有微词 这种情况一直到Java 才有较大的改观 一系列的多线程工具包被纳入了标准库文件 这些工具包括了一个新的多线程程序的执行框架 使编程人员可方便地协调和调度线程的运行 并且新加入了一些高性能的常用的工具 使程序更容易编写 运行效率更高 本文将分类并结合例子来介绍这些新加的多线程工具
在我们开始介绍Java 里的新Concurrent工具前让我们先来看一下一个用旧的多线程工具编写的程序 这个程序里有一个Server线程 它需要启动两个Component Server线程需等到Component线程完毕后再继续 相同的功能在Synchronizer一章里用新加的工具CountDownLatch有相同的实现 两个程序 孰优孰劣 哪个程序更容易编写 哪个程序更容易理解 相信大家看过之后不难得出结论
public class ServerThread
Object concLock = new Object();
int count = ;
public void runTwoThreads()
//启动两个线程去初始化组件
new Thread(new ComponentThread (this)) start();
new Thread(new ComponentThread (this)) start();
// Wait for other thread
while(count != )
synchronized(concLock)
try
concLock wait();
System out println( Wake up );
catch (InterruptedException ie) //处理异常
System out println( Server is up );
public void callBack()
synchronized(concLock)
count ;
concLock notifyAll();
public static void main(String[] args)
ServerThread server = new ServerThread();
server runTwoThreads();
public class ComponentThread implements Runnable
private ServerThread server;
public ComponentThread (ServerThread server)
this server = server;
public void run()
//做组件初始化的工作
System out println( Do ponent initialization );
server callBack();
三个新加的多线程包
Java 里新加入了三个多线程包 ncurrent ncurrent atomic ncurrent locks
Callable 和 Future接口
Callable是类似于Runnable的接口 实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务 Callable和Runnable有几点不同
以下是Callable的一个例子
public class DoCallStuff implements Callable
private int aInt;
public DoCallStuff(int aInt)
this aInt = aInt;
public String call() throws Exception //*
boolean resultOk = false;
if(aInt == )
resultOk = true;
else if(aInt == )
while(true) //infinite loop
System out println( looping );
Thread sleep( );
else
throw new Exception( Callable terminated with Exception! ); //*
if(resultOk)
return Task done ;
else
return Task failed ;
* : 名为DoCallStuff类实现了Callable
* : call方法的返回值类型为String 这是和类的定义相对应的 并且可以抛出异常
* : call方法可以抛出异常 如加重的斜体字所示
以下是调用DoCallStuff的主程序
import ncurrent ExecutionException;
import ncurrent ExecutorService;
import ncurrent Executors;
import ncurrent Future;
public class Executor
public static void main(String[] args)
//*
DoCallStuff call = new DoCallStuff( );
DoCallStuff call = new DoCallStuff( );
DoCallStuff call = new DoCallStuff( );
//*
ExecutorService es = Executors newFixedThreadPool( );
//*
Future
Future
Future
try
//*
System out println(future get());
//*
Thread sleep( );
System out println( Thread terminated? : + future cancel(true));
//*
System out println(future get());
catch (ExecutionException ex)
ex printStackTrace();
catch (InterruptedException ex)
ex printStackTrace();
* : 定义了几个任务
* : 初始了任务执行工具 任务的执行框架将会在后面解释
* : 执行任务 任务启动时返回了一个Future对象 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作 Future所含的值必须跟Callable所含的值对映 比如说例子中Future
* : 任务 正常执行完毕 future get()会返回线程的值
* : 任务 在进行一个死循环 调用future cancel(true)来中止此线程 传入的参数标明是否可打断线程 true表明可以打断
* : 任务 抛出异常 调用future get()时会引起异常的抛出
运行Executor会有以下运行结果
looping
Task done //*
looping
looping //*
looping
looping
looping
looping
Thread terminated? :true //*
//*
ncurrent ExecutionException: java lang Exception: Callable terminated with Exception!
at ncurrent FutureTask$Sync innerGet(FutureTask java: )
at ncurrent FutureTask get(FutureTask java: )
at concurrent Executor main(Executor java: )
……
* : 任务 正常结束
* : 任务 是个死循环 这是它的打印结果
* : 指示任务 被取消
* : 在执行future get()时得到任务 抛出的异常
新的任务执行架构
在Java 之前启动一个任务是通过调用Thread类的start()方法来实现的 任务的提于交和执行是同时进行的 如果你想对任务的执行进行调度或是控制同时执行的线程数量就需要额外编写代码来完成 里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行 并且可以建立一个类似数据库连接池的线程池来执行任务 这个架构主要有三个接口和其相应的具体类组成 这三个接口是Executor ExecutorService和ScheduledExecutorService 让我们先用一个图来显示它们的关系
图的左侧是接口 图的右侧是这些接口的具体类 注意Executor是没有直接具体实现的
Executor接口
是用来执行Runnable任务的 它只定义一个方法
ExecutorService接口
ExecutorService继承了Executor的方法 并提供了执行Callable任务和中止任务执行的服务 其定义的方法主要有
ScheduledExecutorService接口
在ExecutorService的基础上 ScheduledExecutorService提供了按时间安排执行任务的功能 它提供的方法主要有
代码 ScheduleExecutorService的例子
public class ScheduledExecutorServiceTest
public static void main(String[] args)
throws InterruptedException ExecutionException
//*
ScheduledExecutorService service = Executors newScheduledThreadPool( );
//*
Runnable task = new Runnable()
public void run()
System out println( Task repeating );
;
//*
final ScheduledFuture future =
service scheduleAtFixedRate(task TimeUnit SECONDS);
//*
ScheduledFuture
public String call()
future cancel(true);
return task cancelled! ;
TimeUnit SECONDS);
System out println(future get());
//*
service shutdown();
这个例子有两个任务 第一个任务每隔一秒打印一句 Task repeating 第二个任务在 秒钟后取消第一个任务
* : 初始化一个ScheduledExecutorService对象 这个对象的线程池大小为
* : 用内函数的方式定义了一个Runnable任务
* : 调用所定义的ScheduledExecutorService对象来执行任务 任务每秒执行一次 能重复执行的任务一定是Runnable类型 注意我们可以用TimeUnit来制定时间单位 这也是Java 里新的特征 以前的记时单位是微秒 现在可精确到奈秒
* : 调用ScheduledExecutorService对象来执行第二个任务 第二个任务所作的就是在 秒钟后取消第一个任务
* : 关闭服务
Executors类
虽然以上提到的接口有其实现的具体类 但为了方便Java 建议使用Executors的工具类来得到Executor接口的具体对象 需要注意的是Executors是一个类 不是Executor的复数形式 Executors提供了以下一些static的方法
以下是得到和使用ExecutorService的例子
代码 如何调用Executors来获得各种服务对象
//Single Threaded ExecutorService
ExecutorService singleThreadeService = Executors newSingleThreadExecutor();
//Cached ExecutorService
ExecutorService cachedService = Executors newCachedThreadPool();
//Fixed number of ExecutorService
ExecutorService fixedService = Executors newFixedThreadPool( );
//Single ScheduledExecutorService
ScheduledExecutorService singleScheduledService =
Executors newSingleThreadScheduledExecutor();
//Fixed number of ScheduledExecutorService
ScheduledExecutorService fixedScheduledService =
Executors newScheduledThreadPool( );
Lockers和Condition接口
在多线程编程里面一个重要的概念是锁定 如果一个资源是多个线程共享的 为了保证数据的完整性 在进行事务性操作时需要将共享资源锁定 这样可以保证在做事务性操作时只有一个线程能对资源进行操作 从而保证数据的完整性 在 以前 锁定的功能是由Synchronized关键字来实现的 这样做存在几个问题
在Java 里出现两种锁的工具可供使用 下图是这两个工具的接口及其实现
Lock接口
ReentrantLock是Lock的具体类 Lock提供了以下一些方法
代码
//生成一个锁
Lock lock = new ReentrantLock();
public void accessProtectedResource()
lock lock(); //取得锁定
try
//对共享资源进行操作
finally
//一定记着把锁取消掉 锁本身是不会自动解锁的
lock unlock();
ReadWriteLock接口
为了提高效率有些共享资源允许同时进行多个读的操作 但只允许一个写的操作 比如一个文件 只要其内容不变可以让多个线程同时读 不必做排他的锁定 排他的锁定只有在写的时候需要 以保证别的线程不会看到数据不完整的文件 ReadWriteLock可满足这种需要 ReadWriteLock内置两个Lock 一个是读的Lock 一个是写的Lock 多个线程可同时得到读的Lock 但只有一个线程能得到写的Lock 而且写的Lock被锁定后 任何线程都不能得到Lock ReadWriteLock提供的方法有
ReadWriteLock的例子
public class FileOperator
//初始化一个ReadWriteLock
ReadWriteLock lock = new ReentrantReadWriteLock();
public String read()
//得到readLock并锁定
Lock readLock = lock readLock();
readLock lock();
try
//做读的工作
return Read something ;
finally
readLock unlock();
public void write(String content)
//得到writeLock并锁定
Lock writeLock = lock writeLock();
writeLock lock();
try
//做读的工作
finally
writeLock unlock();
需要注意的是ReadWriteLock提供了一个高效的锁定机理 但最终程序的运行效率是和程序的设计息息相关的 比如说如果读的线程和写的线程同时在等待 要考虑是先发放读的lock还是先发放写的lock 如果写发生的频率不高 而且快 可以考虑先给写的lock 还要考虑的问题是如果一个写正在等待读完成 此时一个新的读进来 是否要给这个新的读发锁 如果发了 可能导致写的线程等很久 等等此类问题在编程时都要给予充分的考虑
Condition接口
有时候线程取得lock后需要在一定条件下才能做某些工作 比如说经典的Producer和Consumer问题 Consumer必须在篮子里有苹果的时候才能吃苹果 否则它必须暂时放弃对篮子的锁定 等到Producer往篮子里放了苹果后再去拿来吃 而Producer必须等到篮子空了才能往里放苹果 否则它也需要暂时解锁等Consumer把苹果吃了才能往篮子里放苹果 在Java 以前 这种功能是由Object类的wait() notify()和notifyAll()等方法实现的 在 里面 这些功能集中到了Condition这个接口来实现 Condition提供以下方法
Condition的例子
public class Basket
Lock lock = new ReentrantLock();
//产生Condition对象
Condition produced = lock newCondition();
Condition consumed = lock newCondition();
boolean available = false;
public void produce() throws InterruptedException
lock lock();
try
if(available)
consumed await(); //放弃lock进入睡眠
/*生产苹果*/
System out println( Apple produced );
available = true;
produced signal(); //发信号唤醒等待这个Condition的线程
finally
lock unlock();
public void consume() throws InterruptedException
lock lock();
try
if(!available)
produced await();//放弃lock进入睡眠
/*吃苹果*/
System out println( Apple consumed );
available = false;
consumed signal();//发信号唤醒等待这个Condition的线程
finally
lock unlock();
ConditionTester:
public class ConditionTester
public static void main(String[] args) throws InterruptedException
final Basket basket = new Basket();
//定义一个producer
Runnable producer = new Runnable()
public void run()
try
basket produce();
catch (InterruptedException ex)
ex printStackTrace();
;
//定义一个consumer
Runnable consumer = new Runnable()
public void run()
try
nsume();
catch (InterruptedException ex)
ex printStackTrace();
;
//各产生 个consumer和producer
ExecutorService service = Executors newCachedThreadPool();
for(int i= ; i < ; i++)
service submit(consumer);
Thread sleep( );
for(int i= ; i< ; i++)
service submit(producer);
service shutdown();
: Synchronizer 同步装置
Java 里新加了 个协调线程间进程的同步装置 它们分别是Semaphore CountDownLatch CyclicBarrier和Exchanger
Semaphore:
用来管理一个资源池的工具 Semaphore可以看成是个通行证 线程要想从资源池拿到资源必须先拿到通行证 Semaphore提供的通行证数量和资源池的大小一致 如果线程暂时拿不到通行证 线程就会被阻断进入等待状态 以下是一个例子
public class Pool
ArrayList
Semaphore pass = null;
public Pool(int size)
//初始化资源池
pool = new ArrayList
for(int i= ; i
pool.add("Resource "+i);
//Semaphore的大小和资源池的大小一致
pass = new Semaphore(size);
public String get() throws InterruptedException
//获取通行证,只有得到通行证后才能得到资源
pass.acquire();
return getResource();
public void put(String resource)
//归还通行证,并归还资源
pass.release();
releaseResource(resource);
private synchronized String getResource()
String result = pool.get(0);
pool.remove(0);
System.out.println("Give out "+result);
return result;
private synchronized void releaseResource(String resource)
System.out.println("return "+resource);
pool.add(resource);
SemaphoreTest:
public class SemaphoreTest
public static void main(String[] args)
final Pool aPool = new Pool(2);
Runnable worker = new Runnable()
public void run()
String resource = null;
try
//取得resource
resource = aPool.get();
catch (InterruptedException ex)
ex.printStackTrace();
//用resource做工作
System.out.println("I worked on "+resource);
//归还resource
aPool.put(resource);
;
ExecutorService service = Executors.newCachedThreadPool();
for(int i=0; i<20; i++)
service.submit(worker);
service.shutdown();
CountDownLatch:
CountDownLatch是个计数器,它有一个初始数,等待这个计数器的线程必须等到计数器倒数到零时才可继续。WinGWit比如说一个Server启动时需要初始化4个部件,Server可以同时启动4个线程去初始化这4个部件,然后调用CountDownLatch(4).await()阻断进入等待,每个线程完成任务后会调用一次untDown()来倒计数, 当4个线程都结束时CountDownLatch的计数就会降低为0,此时Server就会被唤醒继续下一步操作。CountDownLatch的方法主要有:
CountDownLatch的例子:一个server调了三个ComponentThread分别去启动三个组件,然后server等到组件都启动了再继续。
public class Server
public static void main(String[] args) throws InterruptedException
System.out.println("Server is starting.");
//初始化一个初始值为3的CountDownLatch
CountDownLatch latch = new CountDownLatch(3);
//起3个线程分别去启动3个组件
ExecutorService service = Executors.newCachedThreadPool();
service.submit(new ComponentThread(latch, 1));
service.submit(new ComponentThread(latch, 2));
service.submit(new ComponentThread(latch, 3));
service.shutdown();
//进入等待状态
latch.await();
//当所需的三个组件都完成时,Server就可继续了
System.out.println("Server is up!");
public class ComponentThread implements Runnable
CountDownLatch latch;
int ID;
/** Creates a new instance of ComponentThread */
public ComponentThread(CountDownLatch latch, int ID)
this.latch = latch;
this.ID = ID;
public void run()
System.out.println("Component "+ID + " initialized!");
//将计数减一
untDown();
运行结果:
Server is starting.
Component 1 initialized!
Component 3 initialized!
Component 2 initialized!
Server is up!
CyclicBarrier:
CyclicBarrier类似于CountDownLatch也是个计数器,不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数,当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续。CyclicBarrier就象它名字的意思一样,可看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍。CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
CyclicBarrier提供以下几个方法:
以下是使用CyclicBarrier的一个例子:两个线程分别在一个数组里放一个数,当这两个线程都结束后,主线程算出数组里的数的和(这个例子比较无聊,我没有想到更合适的例子)
public class MainThread
public static void main(String[] args)
throws InterruptedException, BrokenBarrierException, TimeoutException
final int[] array = new int[2];
CyclicBarrier barrier = new CyclicBarrier(2,
new Runnable() //在所有线程都到达Barrier时执行
public void run()
System.out.println("Total is:"+(array[0]+array[1]));
);
//启动线程
new Thread(new ComponentThread(barrier, array, 0)).start();
new Thread(new ComponentThread(barrier, array, 1)).start();
public class ComponentThread implements Runnable
CyclicBarrier barrier;
int ID;
int[] array;
public ComponentThread(CyclicBarrier barrier, int[] array, int ID)
this.barrier = barrier;
this.ID = ID;
this.array = array;
public void run()
try
array[ID] = new Random().nextInt();
System.out.println(ID+ " generates:"+array[ID]);
//该线程完成了任务等在Barrier处
barrier.await();
catch (BrokenBarrierException ex)
ex.printStackTrace();
catch (InterruptedException ex)
ex.printStackTrace();
Exchanger:
顾名思义Exchanger让两个线程可以互换信息。用一个例子来解释比较容易。例子中服务生线程往空的杯子里倒水,顾客线程从装满水的杯子里喝水,然后通过Exchanger双方互换杯子,服务生接着往空杯子里倒水,顾客接着喝水,然后交换,如此周而复始。
class FillAndEmpty
//初始化一个Exchanger,并规定可交换的信息类型是DataCup
Exchanger
Cup initialEmptyCup = ...; //初始化一个空的杯子
Cup initialFullCup = ...; //初始化一个装满水的杯子
//服务生线程
class Waiter implements Runnable
public void run()
Cup currentCup = initialEmptyCup;
try
//往空的杯子里加水
currentCup.addWater();
//杯子满后和顾客的空杯子交换
currentCup = exchanger.exchange(currentCup);
catch (InterruptedException ex) ... handle ...
//顾客线程
class Customer implements Runnable
public void run()
DataCup currentCup = initialFullCup;
try
//把杯子里的水喝掉
currentCup.drinkFromCup();
//将空杯子和服务生的满杯子交换
currentCup = exchanger.exchange(currentCup);
catch (InterruptedException ex) ... handle ...
void start()
new Thread(new Waiter()).start();
new Thread(new Customer()).start();
6: BlockingQueue接口
BlockingQueue是一种特殊的Queue,若BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态直到BlocingkQueue进了新货才会被唤醒。同样,如果BlockingQueue是满的任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有新的空间才会被唤醒继续操作。BlockingQueue提供的方法主要有:
根据不同的需要BlockingQueue有4种具体实现:
下面是用BlockingQueue来实现Producer和Consumer的例子:
public class BlockingQueueTest
static BlockingQueue
public BlockingQueueTest()
//定义了一个大小为2的BlockingQueue,也可根据需要用其他的具体类
basket = new ArrayBlockingQueue
class Producor implements Runnable
public void run()
while(true)
try
//放入一个对象,若basket满了,等到basket有位置
basket.put("An apple");
catch (InterruptedException ex)
ex.printStackTrace();
class Consumer implements Runnable
public void run()
while(true)
try
//取出一个对象,若basket为空,等到basket有东西为止
String result = basket.take();
catch (InterruptedException ex)
ex.printStackTrace();
public void execute()
for(int i=0; i<10; i++)
new Thread(new Producor()).start();
new Thread(new Consumer()).start();
public static void main(String[] args)
BlockingQueueTest test = new BlockingQueueTest();
test.execute();
7:Atomics 原子级变量
原子量级的变量,主要的类有AtomicBoolean, AtomicInteger, AotmicIntegerArray, AtomicLong, AtomicLongArray, AtomicReference ……。这些原子量级的变量主要提供两个方法:
这些原子级变量利用了现代处理器(CPU)的硬件支持可把两步操作合为一步的功能,避免了不必要的锁定,提高了程序的运行效率。
8:Concurrent Collections 共点聚集
cha138/Article/program/Java/gj/201311/27633相关参考
超线程多核心下Java多线程编程分析 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 一Java环
Java多线程编程基础之线程对象 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! &nbs
超线程多核心下Java多线程编程技术分析[4] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nb
超线程多核心下Java多线程编程技术分析[3] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nb
超线程多核心下Java多线程编程技术分析[1] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nb
超线程多核心下Java多线程编程技术分析[2] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nb
Java多线程编程 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 作者natrium 一理
Java多线程编程的常见陷阱 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 在构造函数中启动线程
实战体会Java多线程编程精要 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 在Java程序中使
实战体会Java多线程编程的精要 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 在Java程序中