多线程.docx

上传人:b****5 文档编号:8170504 上传时间:2023-01-29 格式:DOCX 页数:61 大小:791.80KB
下载 相关 举报
多线程.docx_第1页
第1页 / 共61页
多线程.docx_第2页
第2页 / 共61页
多线程.docx_第3页
第3页 / 共61页
多线程.docx_第4页
第4页 / 共61页
多线程.docx_第5页
第5页 / 共61页
点击查看更多>>
下载资源
资源描述

多线程.docx

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

多线程.docx

多线程

线程

线程状态

1.新建状态(New):

新创建了一个线程对象。

2.就绪状态(Runnable):

线程对象创建后,其他线程调用了该对象的start()方法。

该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3.运行状态(Running):

就绪状态的线程获取了CPU,执行程序代码。

4.阻塞状态(Blocked):

阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。

直到线程进入就绪状态,才有机会转到运行状态。

阻塞的情况分三种:

(一)、等待阻塞(WAITING):

运行的线程执行wait()方法,JVM会把该线程放入等待池中。

join()不带超时时间也会进入WAITING状态。

(二)、同步阻塞(BLOCKED):

运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

线程被notify或notifyAll唤醒以后也会进入BLOCKED状态等待对象锁。

(三)、其他阻塞(TIMED-WAITING):

运行的线程执行sleep()或带有超时时间的join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。

当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5.死亡状态(Dead):

线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

如:

sleep阻塞时,用jstack看到状态如下:

被synchronized阻塞的线程状态如下:

创建线程用Thread的子类或者实现Runnable接口的类。

停止线程

stop(已废弃)

停止线程有stop方法,不过不要使用,太暴力,容易出现数据不一致等问题。

 

interrupt中断

中断线程通过调用线程的interrupt方法。

如果线程是RUNNABLE状态,则会对线程设置一个中断标志,然后实际的中断还是要程序中通过标志来判断自行处理。

如:

publicstaticvoidmain(String[]args)throwsInterruptedException{

Threadt1=newThread(){

@Override

publicvoidrun(){

while(true){

Thread.yield();

}

}

};

t1.start();

Thread.sleep(2000);

t1.interrupt();

}

这里执行了t1.interrupt();并不会让t1线程中断。

改成如下即可:

publicstaticvoidmain(String[]args)throwsInterruptedException{

Threadt1=newThread(){

@Override

publicvoidrun(){

while(true){

System.out.println("kkk");

Thread.yield();

if(this.isInterrupted()){

break;

}

}

}

};

t1.start();

Thread.sleep(2000);

t1.interrupt();

}

如果线程正在WAITING、TIME-WAITING状态,收到interrupt则会抛出InterruptedException异常,不会设置标志位。

而在BLOCKED状态则要区分,普通BLOCKED状态不会抛异常,如果是等待ReentrantLock的lockInterruptibly锁,则会抛出异常

状态切换

wait、notify、notifyAll

wait、notify、notifyAll是对象上的方法,一个线程调用了某个对象wait方法,当前线程会进入阻塞状态(WAITING),加入到对象的阻塞队列中,一个对象的阻塞队列可能有多个线程,调用对象的notify方法会从阻塞队列中随机唤醒一个线程,唤醒的线程会进入blocked状态,等待对象的锁,notifyAll会唤醒该对象阻塞队列的所有阻塞线程。

调用wait和notify方法前必须获得对象的锁,直接调用是不行的,如:

publicclassTest3{

publicstaticvoidmain(String[]args)throwsInterruptedException{

Objecto=newObject();

o.wait();

}

}

会报错

所以一般都在synchronize中调用,改为如下即可:

publicclassTest3{

publicstaticvoidmain(String[]args)throwsInterruptedException{

Objecto=newObject();

synchronized(o){

o.wait();

}

}

}

 

唤醒的线程不会直接向下执行,而是进入blocked状态,等待拿到对象的锁以后才进入往下执行。

如:

publicclassTest2{

staticObjecto=newObject();

publicstaticvoidmain(String[]args)throwsInterruptedException{

Threadt1=newThread(){

@Override

publicvoidrun(){

try{

synchronized(o){

o.wait();

}

System.out.println("t1end");

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

};

t1.start();

Thread.sleep(1000);

synchronized(o){

o.notify();

System.out.println("t1notified");

Thread.sleep(2000);

System.out.println("mainend");

}

}

}

输出:

t1notified

mainend

t1end

其中t1唤醒之后并没有马上执行,而是等待主线程执行完synchronize方法释放锁以后才执行。

调用wait方法会释放当前的锁,而sleep方法不会释放锁。

suspend、resume(已废弃)

suspend会挂起线程,resume恢复线程,挂起的线程状态还是RUNNABLE,如下:

由于挂起线程代码一般是在本线程中执行,而线程挂起之后就能通过其他线程来执行恢复本线程的代码,而线程间的先后顺序往往不能保证,很容易导致resume先于suspend执行,这样挂起的线程就无法恢复了,所以此方式已被废弃使用。

join、yield

join等待一个线程执行完,本线程再执行,用于一个线程依赖另一个线程的执行结果的情况,如:

publicclassTest2{

staticvolatileinta=0;

publicstaticvoidmain(String[]args)throwsInterruptedException{

Threadt1=newThread(){

publicvoidrun(){

for(inti=0;i<1000000;i++){

a++;

}

};

};

t1.start();

t1.join();//等待t1线程累加完再打印a,否则打印的a可能会小于1000000

System.out.println(a);//结果打印出1000000

}

}

yield会让线程从运行状态转为就绪状态,重新争夺CPU资源,用于优先级低的线程让出CPU资源。

volatile

见”jvm.docx”

线程组

把多个线程归成一个组,起个组名,统一管理。

如:

publicclassTest3{

publicstaticvoidmain(String[]args)throwsInterruptedException{

ThreadGroupthreadGroup=newThreadGroup("threadGroup");

Threadt1=newThread(threadGroup,"t1"){

@Override

publicvoidrun(){

while(true){

try{

Thread.sleep(3000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

System.out.println(this.getThreadGroup().getName()+"-"+this.getName());

}

}

};

Threadt2=newThread(threadGroup,"t2"){

@Override

publicvoidrun(){

while(true){

try{

Thread.sleep(3000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

System.out.println(this.getThreadGroup().getName()+"-"+this.getName());

}

}

};

t1.start();

t2.start();

threadGroup.list();

}

}

打印:

java.lang.ThreadGroup[name=threadGroup,maxpri=10]

Thread[t1,5,threadGroup]

Thread[t2,5,threadGroup]

threadGroup-t1

threadGroup-t2

threadGroup-t1

threadGroup-t2

……

守护线程

当一个Java应用内,所有非守护进程都结束时,Java虚拟机就会自然退出,守护线程自动结束

如:

/**

*守护线程测试

*@authorAdministrator

*

*当一个Java应用内,所有非守护进程都结束时,Java虚拟机就会自然退出,守护线程自动结束

*

*/

publicclassDaemonThreadTest{

publicstaticclassDaemonThreadextendsThread{

@Override

publicvoidrun(){

System.out.println("start");

try{

Thread.sleep(2000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

System.out.println("end");//这句代码没有执行,因为主线程已经提前执行完了

}

}

publicstaticvoidmain(String[]args)throwsInterruptedException{

DaemonThreaddt=newDaemonThread();

dt.setDaemon(true);

dt.start();

Thread.sleep(1000);

}

}

输出:

start

其中dt.setDaemon(true);要先于start()方法,执行,否则抛出如下异常:

而线程会被当做非守护线程继续执行,此时打印了end。

线程优先级

优先级越高,争夺到CPU的概率越高。

/**

*测试线程优先级,优先级高的线程先执行完的概率大

*@authorAdministrator

*

*/

publicclassPriorityTest{

publicstaticclassHighextendsThread{

staticintcount=0;

@Override

publicvoidrun(){

while(true){

synchronized(PriorityTest.class){

count++;

if(count>10000000){

System.out.println("High");

break;

}

}

}

}

}

publicstaticclassLowextendsThread{

staticintcount=0;

@Override

publicvoidrun(){

while(true){

synchronized(PriorityTest.class){

count++;

if(count>10000000){

System.out.println("Low");

break;

}

}

}

}

}

publicstaticvoidmain(String[]args)throwsInterruptedException{

Highhigh=newHigh();

Lowlow=newLow();

//设置线程优先级

high.setPriority(Thread.MAX_PRIORITY);

low.setPriority(Thread.MIN_PRIORITY);

low.start();

high.start();

}

}

多次试验之后High先执行完的概率高于Low

synchronized

见”jvm.docx”

注意:

不要锁Integer等包装对象,因为它的值一旦变化,就会自动创建一个新对象。

ArrayList、HashMap的并发问题

见”jvm.docx”

Vector、ConcurrentHashMap是线程安全的

队列

概念

Queue是jdk1.5以后提供的队列接口,与List、Set平级,都是Collection的子接口

Queue队列先进先出,从队尾进,队首出。

队列的方法:

add、offer都是入列,队列满时,add抛出异常,offer返回false。

remove、poll都是出列,返回出列的值,队列空时,remove抛出异常,poll返回null

element、peek都是返回队首值,不出列,队列空时,element抛出异常,peek返回null

ps:

由于poll和peek方法出错就返回null。

因此,尽量不要向队列插入null值。

使用,如:

publicclassQueueTest{

publicstaticvoidmain(String[]args){

//add()和remove()方法在失败的时候会抛出异常(不推荐)

Queuequeue=newLinkedList();

//添加元素(入列)

queue.offer("a");//如果队列已满,返回false

queue.add("b");//如果队列已满,会抛出异常

for(Stringq:

queue){

System.out.println(q);

}

//获取第一个元素,不删除

System.out.println("peek="+queue.peek());//返回第一个元素,队列如果为空,返回null

//遍历队列

for(Stringq:

queue){

System.out.println(q);

}

//删除元素(出列)

System.out.println("poll1="+queue.poll());//返回第一个元素,并在队列中删除,如果队列为空,返回null

System.out.println("poll2="+queue.remove());//返回第一个元素,并在队列中删除,果队列为空,返回抛出异常

System.out.println("poll3="+queue.poll());//队列为空,返回null

//获取第一个元素,不删除

System.out.println("peek="+queue.peek());//返回第一个元素,队列如果为空,返回null

System.out.println("element="+queue.element());//返回第一个元素,队列如果为空,抛出异常

}

}

Queue的子类

Queue有许多实现,如:

双向队列Deque

双向队列,提供addFirst、addLast、offerFirst、offerLast等可以从队首队尾操作的方法。

线程安全队列ConcurrentLinkedQueue

采用无锁的volatile做的,效率很高

阻塞队列BlockingQueue(线程安全)

实现阻塞接口BlockingQueue的队列,如:

ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、DelayQueue

阻塞队列是线程安全的,内部用ReentrantLock保证,如:

阻塞接口的Queue有put入列,take出列,put时如果队列已满,会阻塞等待,take时队列如果为空,也会阻塞等待。

通常用于生产者、消费者模式。

ArrayBlockingQueue:

创建时必须指定大小。

LinkedBlockingQueue:

创建时可不指定大小,如果没有指定大小,则put时,只有容量达到Integer.MAX_VALUE才会阻塞。

/**

*阻塞队列测试

*@authorAdministrator

*

*/

publicclassQueueTest{

staticBlockingQueueblockingQueue=newArrayBlockingQueue

(2);

publicstaticvoidmain(String[]args)throwsInterruptedException{

newThread(newRunnable(){

@Override

publicvoidrun(){

try{

Thread.sleep(2000);//新线程启动后先sleep2秒。

blockingQueue.put(123);

System.out.println("branchthreadend");

}catch(Exceptione){

e.printStackTrace();

}

}

}).start();

//take空队列会阻塞当前线程,等待队列put完后才继续执行

blockingQueue.take();

System.out.println("mainthreadend");

}

}

输出:

branchthreadend

mainthreadend

SynchronousQueue

一种特殊的BlockingQueue,put和take必须交替执行,其中每个put必须等待一个take,反之亦然。

*iterator()永远返回空,因为里面没东西。

 

   *peek()永远返回null。

 

   *put()往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走。

 

   *offer()往queue里放一个element后立即返回,如果碰巧这个element被另一个thread取走了,offer方法返回true,认为offer成功;否则返回false。

 

   *offer(2000,TimeUnit.SECONDS)往queue里放一个element但是等待指定的时间后才返回,返回的逻辑和offer()方法一样。

 

   *take()取出并且remove掉queue里的element(认为是在queue里的。

),取不到东西他会一直等。

 

   *poll()取出并且remove掉queue里的element(认为是在queue里的。

),只有到碰巧另外一个线程正在往queue里offer数据或者put数据的时候,该方法才会取到东西。

否则立即返回null。

 

   *poll(2000,TimeUnit.SECONDS)等待指定的时间然后取出并且remove掉queue里的element,其实就是再等其他的thread来往里塞。

 

   *isEmpty()永远是true。

 

   *remainingCapacity()永远是0。

 

   *remove()和removeAll()永远是false。

 

优先级队列PriorityQueue

每次入列,整个队列都会重新排序,出列时按排序后的顺序出列,不能有null元素(会抛空指针异常),使用如:

/**

*优先级列队测试

*@authorAdministrator

*

*/

publicclassQueueTest{

publicstaticvoidmain(String[]args)throwsInterruptedExcepti

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

当前位置:首页 > 教学研究 > 教学反思汇报

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

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