java线程大总结.docx
《java线程大总结.docx》由会员分享,可在线阅读,更多相关《java线程大总结.docx(29页珍藏版)》请在冰豆网上搜索。
java线程大总结
二、
一、
从结果可以看出,信号量仅仅是对池资源进行监控,但不保证线程的安全,因此,在使用时候,应该自己控制线程的安全访问池资源。
阻塞队列是Java5线程新特征中的内容,Java定义了阻塞队列的接口java.util.concurrent.BlockingQueue,阻塞队列的概念是,一个指定长度的队列,如果队列满了,添加新元素的操作会被阻塞等待,直到有空位为止。
同样,当队列为空时候,请求队列元素的操作同样会阻塞等待,直到有可用元素为止。
有了这样的功能,就为多线程的排队等候的模型实现开辟了便捷通道,非常有用。
java.util.concurrent.BlockingQueue继承了java.util.Queue接口,可以参看API文档。
下面给出一个简单应用的例子:
importjava.util.concurrent.BlockingQueue;
importjava.util.concurrent.ArrayBlockingQueue;
/**
*Java线程:
新特征-阻塞队列
*
*@authorleizhimin2009-11-514:
59:
15
*/
publicclassTest{
publicstaticvoidmain(String[]args)throwsInterruptedException{
BlockingQueuebqueue=newArrayBlockingQueue(20);
for(inti=0;i<30;i++){
//将指定元素添加到此队列中,如果没有可用空间,将一直等待(如果有必要)。
bqueue.put(i);
System.out.println("向阻塞队列中添加了元素:
"+i);
}
System.out.println("程序到此运行结束,即将退出----");
}
}
输出结果:
向阻塞队列中添加了元素:
0
向阻塞队列中添加了元素:
1
向阻塞队列中添加了元素:
2
向阻塞队列中添加了元素:
3
向阻塞队列中添加了元素:
4
向阻塞队列中添加了元素:
5
向阻塞队列中添加了元素:
6
向阻塞队列中添加了元素:
7
向阻塞队列中添加了元素:
8
向阻塞队列中添加了元素:
9
向阻塞队列中添加了元素:
10
向阻塞队列中添加了元素:
11
向阻塞队列中添加了元素:
12
向阻塞队列中添加了元素:
13
向阻塞队列中添加了元素:
14
向阻塞队列中添加了元素:
15
向阻塞队列中添加了元素:
16
向阻塞队列中添加了元素:
17
向阻塞队列中添加了元素:
18
向阻塞队列中添加了元素:
19
可以看出,输出到元素19时候,就一直处于等待状态,因为队列满了,程序阻塞了。
这里没有用多线程来演示,没有这个必要。
另外,阻塞队列还有更多实现类,用来满足各种复杂的需求:
ArrayBlockingQueue,DelayQueue,LinkedBlockingQueue,PriorityBlockingQueue,SynchronousQueue,具体的API差别也很小。
对于阻塞栈,与阻塞队列相似。
不同点在于栈是“后入先出”的结构,每次操作的是栈顶,而队列是“先进先出”的结构,每次操作的是队列头。
这里要特别说明一点的是,阻塞栈是Java6的新特征。
Java为阻塞栈定义了接口:
java.util.concurrent.BlockingDeque,其实现类也比较多,具体可以查看JavaAPI文档。
下面看一个简单例子:
importjava.util.concurrent.BlockingDeque;
importjava.util.concurrent.LinkedBlockingDeque;
/**
*Java线程:
新特征-阻塞栈
*
*@authorleizhimin2009-11-515:
34:
29
*/
publicclassTest{
publicstaticvoidmain(String[]args)throwsInterruptedException{
BlockingDequebDeque=newLinkedBlockingDeque(20);
for(inti=0;i<30;i++){
//将指定元素添加到此阻塞栈中,如果没有可用空间,将一直等待(如果有必要)。
bDeque.putFirst(i);
System.out.println("向阻塞栈中添加了元素:
"+i);
}
System.out.println("程序到此运行结束,即将退出----");
}
}
输出结果:
向阻塞栈中添加了元素:
0
向阻塞栈中添加了元素:
1
向阻塞栈中添加了元素:
2
向阻塞栈中添加了元素:
3
向阻塞栈中添加了元素:
4
向阻塞栈中添加了元素:
5
向阻塞栈中添加了元素:
6
向阻塞栈中添加了元素:
7
向阻塞栈中添加了元素:
8
向阻塞栈中添加了元素:
9
向阻塞栈中添加了元素:
10
向阻塞栈中添加了元素:
11
向阻塞栈中添加了元素:
12
向阻塞栈中添加了元素:
13
向阻塞栈中添加了元素:
14
向阻塞栈中添加了元素:
15
向阻塞栈中添加了元素:
16
向阻塞栈中添加了元素:
17
向阻塞栈中添加了元素:
18
向阻塞栈中添加了元素:
19
从上面结果可以看到,程序并没结束,二是阻塞住了,原因是栈已经满了,后面追加元素的操作都被阻塞了。
条件变量是Java5线程中很重要的一个概念,顾名思义,条件变量就是表示条件的一种变量。
但是必须说明,这里的条件是没有实际含义的,仅仅是个标记而已,并且条件的含义往往通过代码来赋予其含义。
这里的条件和普通意义上的条件表达式有着天壤之别。
条件变量都实现了java.util.concurrent.locks.Condition接口,条件变量的实例化是通过一个Lock对象上调用newCondition()方法来获取的,这样,条件就和一个锁对象绑定起来了。
因此,Java中的条件变量只能和锁配合使用,来控制并发程序访问竞争资源的安全。
条件变量的出现是为了更精细控制线程等待与唤醒,在Java5之前,线程的等待与唤醒依靠的是Object对象的wait()和notify()/notifyAll()方法,这样的处理不够精细。
而在Java5中,一个锁可以有多个条件,每个条件上可以有多个线程等待,通过调用await()方法,可以让线程在该条件下等待。
当调用signalAll()方法,又可以唤醒该条件下的等待的线程。
有关Condition接口的API可以具体参考JavaAPI文档。
条件变量比较抽象,原因是他不是自然语言中的条件概念,而是程序控制的一种手段。
下面以一个银行存取款的模拟程序为例来揭盖Java多线程条件变量的神秘面纱:
有一个账户,多个用户(线程)在同时操作这个账户,有的存款有的取款,存款随便存,取款有限制,不能透支,任何试图透支的操作都将等待里面有足够存款才执行操作。
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.locks.Condition;
importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
/**
*Java线程:
条件变量
*
*@authorleizhimin2009-11-510:
57:
29
*/
publicclassTest{
publicstaticvoidmain(String[]args){
//创建并发访问的账户
MyCountmyCount=newMyCount("95599200901215522",10000);
//创建一个线程池
ExecutorServicepool=Executors.newFixedThreadPool
(2);
Threadt1=newSaveThread("张三",myCount,2000);
Threadt2=newSaveThread("李四",myCount,3600);
Threadt3=newDrawThread("王五",myCount,2700);
Threadt4=newSaveThread("老张",myCount,600);
Threadt5=newDrawThread("老牛",myCount,1300);
Threadt6=newDrawThread("胖子",myCount,800);
//执行各个线程
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
//关闭线程池
pool.shutdown();
}
}
/**
*存款线程类
*/
classSaveThreadextendsThread{
privateStringname;//操作人
privateMyCountmyCount;//账户
privateintx;//存款金额
SaveThread(Stringname,MyCountmyCount,intx){
this.name=name;
this.myCount=myCount;
this.x=x;
}
publicvoidrun(){
myCount.saving(x,name);
}
}
/**
*取款线程类
*/
classDrawThreadextendsThread{
privateStringname;//操作人
privateMyCountmyCount;//账户
privateintx;//存款金额
DrawThread(Stringname,MyCountmyCount,intx){
this.name=name;
this.myCount=myCount;
this.x=x;
}
publicvoidrun(){
myCount.drawing(x,name);
}
}
/**
*普通银行账户,不可透支
*/
classMyCount{
privateStringoid;//账号
privateintcash;//账户余额
privateLocklock=newReentrantLock();//账户锁
privateCondition_save=lock.newCondition();//存款条件
privateCondition_draw=lock.newCondition();//取款条件
MyCount(Stringoid,intcash){
this.oid=oid;
this.cash=cash;
}
/**
*存款
*
*@paramx操作金额
*@paramname操作人
*/
publicvoidsaving(intx,Stringname){
lock.lock();//获取锁
if(x>0){
cash+=x;//存款
System.out.println(name+"存款"+x+",当前余额为"+cash);
}
_draw.signalAll();//唤醒所有等待线程。
lock.unlock();//释放锁
}
/**
*取款
*
*@paramx操作金额
*@paramname操作人
*/
publicvoiddrawing(intx,Stringname){
lock.lock();//获取锁
try{
if(cash-x<0){
_draw.await();//阻塞取款操作
}else{
cash-=x;//取款
System.out.println(name+"取款"+x+",当前余额为"+cash);
}
_save.signalAll();//唤醒所有存款操作
}catch(InterruptedExceptione){
e.printStackTrace();
}finally{
lock.unlock();//释放锁
}
}
}
李四存款3600,当前余额为13600
张三存款2000,当前余额为15600
老张存款600,当前余额为16200
老牛取款1300,当前余额为14900
胖子取款800,当前余额为14100
王五取款2700,当前余额为11400
Processfinishedwithexitcode0
假如我们不用锁和条件变量,如何实现此功能呢?
下面是实现代码:
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
/**
*Java线程:
不用条件变量
*
*@authorleizhimin2009-11-510:
57:
29
*/
publicclassTest{
publicstaticvoidmain(String[]args){
//创建并发访问的账户
MyCountmyCount=newMyCount("95599200901215522",10000);
//创建一个线程池
ExecutorServicepool=Executors.newFixedThreadPool
(2);
Threadt1=newSaveThread("张三",myCount,2000);
Threadt2=newSaveThread("李四",myCount,3600);
Threadt3=newDrawThread("王五",myCount,2700);
Threadt4=newSaveThread("老张",myCount,600);
Threadt5=newDrawThread("老牛",myCount,1300);
Threadt6=newDrawThread("胖子",myCount,800);
//执行各个线程
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
//关闭线程池
pool.shutdown();
}
}
/**
*存款线程类
*/
classSaveThreadextendsThread{
privateStringname;//操作人
privateMyCountmyCount;//账户
privateintx;//存款金额
SaveThread(Stringname,MyCountmyCount,intx){
this.name=name;
this.myCount=myCount;
this.x=x;
}
publicvoidrun(){
myCount.saving(x,name);
}
}
/**
*取款线程类
*/
classDrawThreadextendsThread{
privateStringname;//操作人
privateMyCountmyCount;//账户
privateintx;//存款金额
DrawThread(Stringname,MyCountmyCount,intx){
this.name=name;
this.myCount=myCount;
this.x=x;
}
publicvoidrun(){
myCount.drawing(x,name);
}
}
/**
*普通银行账户,不可透支
*/
classMyCount{
privateStringoid;//账号
privateintcash;//账户余额
MyCount(Stringoid,intcash){
this.oid=oid;
this.cash=cash;
}
/**
*存款
*
*@paramx操作金额
*@paramname操作人
*/
publicsynchronizedvoidsaving(intx,Stringname){
if(x>0){
cash+=x;//存款
System.out.println(name+"存款"+x+",当前余额为"+cash);
}
notifyAll();//唤醒所有等待线程。
}
/**
*取款
*
*@paramx操作金额
*@paramname操作人
*/
publicsynchronizedvoiddrawing(intx,Stringname){
if(cash-x<0){
try{
wait();
}catch(InterruptedExceptione1){
e1.printStackTrace();
}
}else{
cash-=x;//取款
System.out.println(name+"取款"+x+",当前余额为"+cash);
}
notifyAll();//唤醒所有存款操作
}
}
输出结果为:
李四存款3600,当前余额为13600
王五取款2700,当前余额为10900
老张存款600,当前余额为11500
老牛取款1300,当前余额为10200
胖子取款800,当前余额为9400
张三存款2000,当前余额为11400
Processfinishedwithexitcode0
结合先前同步代码知识,举一反三,将此例改为同步代码块来实现,代码如下:
importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
/**
*Java线程:
改为同步代码块
*
*@authorleizhimin2009-11-510:
57:
29
*/
publicclassTest{
publicstaticvoidmain(String[]args){
//创建并发访问的账户
MyCountmyCount=newMyCount("95599200901215522",10000);
//创建一个线程池
ExecutorServicepool=Executors.newFixedThreadPool
(2);
Threadt1=newSaveThread("张三",myCount,2000);
Threadt2=newSaveThread("李四",myCount,3600);
Threadt3=newDrawThread("王五",myCount,2700);
Threadt4=newSaveThread("老张",myCount,600);
Threadt5=newDrawThread("老牛",myCount,1300);
Threadt6=newDrawThread("胖子",myCount,800);
//执行各个线程
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
//关闭线程池
pool.shutdown();
}
}
/**
*存款线程类
*/
classSaveThreadextendsThread{
privateStringname;//操作人
privateMyCountmyCount;//账户
privateintx;//存款金额
SaveThread(Stringname,MyCountmyCount,intx){
this.name=name;
this.myCount=myCount;
this.x=x;
}
publicvoidrun(){
myCount.saving(x,name);
}
}
/**
*取款线程类
*/
classDrawThreadextendsThread{
privateStringname;//操作人
privateMyCountmyCount;//账户
privateintx;//存款金额
DrawThread(Stringname,MyCountmyCount,intx){
this.name=name;
this.myCount=myCount;
this.x=x;
}
publicvoidrun(){
myCount.drawing(x,name);
}
}
/**
*普通银行账户,不可透支
*/
classMyCount{
privateStringoid;//账号
privateintcash;//账户余额
MyCount(Stringoid,intcash){
this.oid=oid;
this.cash=cash;
}
/**
*存款
*
*@paramx操作金额
*@paramna