多线程Word下载.docx
《多线程Word下载.docx》由会员分享,可在线阅读,更多相关《多线程Word下载.docx(61页珍藏版)》请在冰豆网上搜索。
停止线程
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线程中断。
改成如下即可:
System.out.println("
kkk"
);
if(this.isInterrupted()){
break;
}
如果线程正在WAITING、TIME-WAITING状态,收到interrupt则会抛出InterruptedException异常,不会设置标志位。
而在BLOCKED状态则要区分,普通BLOCKED状态不会抛异常,如果是等待ReentrantLock的lockInterruptibly锁,则会抛出异常
状态切换
wait、notify、notifyAll
wait、notify、notifyAll是对象上的方法,一个线程调用了某个对象wait方法,当前线程会进入阻塞状态(WAITING),加入到对象的阻塞队列中,一个对象的阻塞队列可能有多个线程,调用对象的notify方法会从阻塞队列中随机唤醒一个线程,唤醒的线程会进入blocked状态,等待对象的锁,notifyAll会唤醒该对象阻塞队列的所有阻塞线程。
调用wait和notify方法前必须获得对象的锁,直接调用是不行的,如:
publicclassTest3{
Objecto=newObject();
o.wait();
}
会报错
所以一般都在synchronize中调用,改为如下即可:
synchronized(o){
o.wait();
}
唤醒的线程不会直接向下执行,而是进入blocked状态,等待拿到对象的锁以后才进入往下执行。
publicclassTest2{
staticObjecto=newObject();
try{
synchronized(o){
o.wait();
t1end"
}catch(InterruptedExceptione){
e.printStackTrace();
Thread.sleep(1000);
o.notify();
System.out.println("
t1notified"
Thread.sleep(2000);
mainend"
输出:
t1notified
mainend
t1end
其中t1唤醒之后并没有马上执行,而是等待主线程执行完synchronize方法释放锁以后才执行。
调用wait方法会释放当前的锁,而sleep方法不会释放锁。
suspend、resume(已废弃)
suspend会挂起线程,resume恢复线程,挂起的线程状态还是RUNNABLE,如下:
由于挂起线程代码一般是在本线程中执行,而线程挂起之后就能通过其他线程来执行恢复本线程的代码,而线程间的先后顺序往往不能保证,很容易导致resume先于suspend执行,这样挂起的线程就无法恢复了,所以此方式已被废弃使用。
join、yield
join等待一个线程执行完,本线程再执行,用于一个线程依赖另一个线程的执行结果的情况,如:
staticvolatileinta=0;
for(inti=0;
i<
1000000;
i++){
a++;
};
t1.join();
//等待t1线程累加完再打印a,否则打印的a可能会小于1000000
System.out.println(a);
//结果打印出1000000
yield会让线程从运行状态转为就绪状态,重新争夺CPU资源,用于优先级低的线程让出CPU资源。
volatile
见”jvm.docx”
线程组
把多个线程归成一个组,起个组名,统一管理。
ThreadGroupthreadGroup=newThreadGroup("
threadGroup"
Threadt1=newThread(threadGroup,"
t1"
){
try{
Thread.sleep(3000);
}catch(InterruptedExceptione){
e.printStackTrace();
System.out.println(this.getThreadGroup().getName()+"
-"
+this.getName());
Threadt2=newThread(threadGroup,"
t2"
t2.start();
threadGroup.list();
打印:
java.lang.ThreadGroup[name=threadGroup,maxpri=10]
Thread[t1,5,threadGroup]
Thread[t2,5,threadGroup]
threadGroup-t1
threadGroup-t2
……
守护线程
当一个Java应用内,所有非守护进程都结束时,Java虚拟机就会自然退出,守护线程自动结束
/**
*守护线程测试
*@authorAdministrator
*
*当一个Java应用内,所有非守护进程都结束时,Java虚拟机就会自然退出,守护线程自动结束
*
*/
publicclassDaemonThreadTest{
publicstaticclassDaemonThreadextendsThread{
@Override
publicvoidrun(){
start"
try{
Thread.sleep(2000);
}catch(InterruptedExceptione){
e.printStackTrace();
end"
//这句代码没有执行,因为主线程已经提前执行完了
DaemonThreaddt=newDaemonThread();
dt.setDaemon(true);
dt.start();
start
其中dt.setDaemon(true);
要先于start()方法,执行,否则抛出如下异常:
而线程会被当做非守护线程继续执行,此时打印了end。
线程优先级
优先级越高,争夺到CPU的概率越高。
*测试线程优先级,优先级高的线程先执行完的概率大
publicclassPriorityTest{
publicstaticclassHighextendsThread{
staticintcount=0;
while(true){
synchronized(PriorityTest.class){
count++;
if(count>
10000000){
System.out.println("
High"
publicstaticclassLowextendsThread{
Low"
Highhigh=newHigh();
Lowlow=newLow();
//设置线程优先级
high.setPriority(Thread.MAX_PRIORITY);
low.setPriority(Thread.MIN_PRIORITY);
low.start();
high.start();
多次试验之后High先执行完的概率高于Low
synchronized
注意:
不要锁Integer等包装对象,因为它的值一旦变化,就会自动创建一个新对象。
ArrayList、HashMap的并发问题
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()方法在失败的时候会抛出异常(不推荐)
Queue<
String>
queue=newLinkedList<
();
//添加元素(入列)
queue.offer("
a"
//如果队列已满,返回false
queue.add("
b"
//如果队列已满,会抛出异常
for(Stringq:
queue){
System.out.println(q);
//获取第一个元素,不删除
System.out.println("
peek="
+queue.peek());
//返回第一个元素,队列如果为空,返回null
//遍历队列
//删除元素(出列)
poll1="
+queue.poll());
//返回第一个元素,并在队列中删除,如果队列为空,返回null
poll2="
+queue.remove());
//返回第一个元素,并在队列中删除,果队列为空,返回抛出异常
poll3="
//队列为空,返回null
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才会阻塞。
*阻塞队列测试
staticBlockingQueue<
Integer>
blockingQueue=newArrayBlockingQueue<
(2);
newThread(newRunnable(){
Thread.sleep(2000);
//新线程启动后先sleep2秒。
blockingQueue.put(123);
branchthreadend"
}catch(Exceptione){
}).start();
//take空队列会阻塞当前线程,等待队列put完后才继续执行
blockingQueue.take();
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元素(会抛空指针异常),使用如:
*优先级列队测试
publicstaticvoidmain(String[]args)throwsInterruptedExcepti