黑马程序员-线程间的通讯
2014-06-06 17:58 黑马程序员* 阅读(149) 评论(0) 收藏 举报思考:wait(),notify(),notiyfAll()的特点,?
1.这些方法存在与同步中,对持有监视器(锁)的线程操作。
2.使用这些方法时必须要标示所属的同步的锁。
思考:为什么wait(),notify(),notiyfAll(),标示所属的同步的锁
只有同一个锁上的等待的线程,才可以被同一个锁上notiyf唤醒,也就是说,等待和唤醒必须是同一个锁。
所以,这些方法在操作同步中的线程时,都必须要标识它们所操作线程的锁。两伙小伙伴的例子。
思考:wait(),notify(),notiyfAll(),用来操作线程为什么定义在Object类中
锁可以是任意对象,所以任意对象调用的方法应该定义在Object类中。
思考:wait(),sleep()有什么区别?
wait():释放资源,释放锁。
sleep():释放资源,不释放锁。
线程间的通讯:
其实就是多个线程在操作同一个资源,
但是操作的动作不同。
public class Demo23多线程 {
/**练习:存入和取出 mark,man 和丽丽 女(交替存入)
* 出现的问题1:丽丽 男 mark 女
* 原因:设置 丽丽 女时没有实现同步。
* 解决;加同步函数或同步代码块
* 出现问题2;无法实现存入一个,取出一个
* 原因:其中一个线程可能会抢到多次执行权。
* 解决办法;添加标记,wait(),notify()
* 自身错误:if(){wait},后面不可以写else,要让它醒来后继续执行未完成的代码
*/
public static void main(String[] args) { Person23 p = new Person23(); new Thread(new In(p)).start(); new Thread(new Out(p)).start(); } } class Person23{ private String name; private String sex; boolean flag=false; public synchronized void set(String name,String sex) { if(flag){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // else{//犯过多次错。 this.name=name; this.sex=sex; flag=true; this.notify(); // } } public synchronized void get() { if(!flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(name+sex); flag=false; this.notify(); } } class In implements Runnable{ private Person23 p; public In(Person23 p) { super(); this.p = p; } public void run() { int count = 1; while(true){ if (count%2==0) { p.set("丽丽", "女"); } else { p.set("mark", "man"); } count++; } } } class Out implements Runnable{ private Person23 p; public Out(Person23 p) { super(); this.p = p; } public void run() { while(true){ p.get(); } } }
public class ThreadTest8 {
/**练习:消费者和生产者
问题1:当线程超过两个后,会出现,生产一个,消费两个的情况。
原因:if()语句中,wait后有两个线程等待。被唤醒后,导致其中一个没有判断条件,就执行了
所以出现了打印两次的情况。
解决:使用while语句。但是while会导致所有的线程等待,使用notifyAll();
即使唤醒了本方,也不会出现连续生产两个的情况。因为本方被唤醒,会再次判断标记,并等待。
新问题:唤醒本方会导致本方去抢资源,即使抢到了也等待,但是消耗了资源。
解决:1.5新特型Lock。
`
public static void main(String[] args) { Resource8 r = new Resource8(); Custom custom = new Custom(r); Product product = new Product(r); new Thread(custom).start(); new Thread(custom).start(); new Thread(product).start(); new Thread(product).start(); } } //共享资源,生产和消费资源 class Resource8{ private String name; private int num; private boolean flag=false; public void set(String name,int num){ this.name=name; this.num=num; } public int getNum(){ return num; } public String getName(){ return name; } public synchronized void pruduce(String str,int listnum) { while(flag){//超过两个线程后,if判断易出现两个线程等待结束后,其中一个不会判断标记,导致多生产一次(连个线程只判断了一次) //但是用while会导致所有线程等待,所以notifyAll //4个线程会全部等待。2个没事。因为线程唤醒了本方的线程,判断了一次标记后,也等待了。而没有去唤醒对方。 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("线程等待出错"); } } set(str,listnum); System.out.println(Thread.currentThread().getName()+getName()+" 生产...."+getNum()); flag=true; notifyAll(); } public synchronized void consume() { while(!flag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("线程等待出错"); } } System.out.println(Thread.currentThread().getName()+getName()+" 消费....."+getNum()); flag=false; notifyAll(); } } class Product implements Runnable{ private Resource8 res; private int num=1; public Product(Resource8 r) { super(); this.res = r; } public void run() { while(true){ //传入生产的商品的名称和编号,编号从1开始 res.pruduce("商品",num++); //生产超过5000个,停止生产。 } }} class Custom implements Runnable{ private Resource8 res; public Custom(Resource8 res) { super(); this.res = res ; } public void run() { //开始消费 while(true){ res.consume(); } }}
jdk1.5中提供了多线程升级解决方案。java.util.concurrent.locks
接口 Lock
将同步synchroniaed替换成显示的lock操作
将object中的wait,notify,notifyall 替换成了condition对象中await(),signal,signalAll,
该对象可以lock锁,进行获取。
Lock(锁),具有lock和unlock,newcondition方法,
condition包含singal和awiat方法。而且condition可以建立多个。
本实例中,实现了本方只唤醒对方的操作。
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class 多线程新特性 { /**生产者和消费者 * Lock接口。conditon代替synchronized */ public static void main(String[] args) { resource r = new resource(); new Thread(new produce(r)).start(); new Thread(new produce(r)).start(); new Thread(new custom(r)).start(); new Thread(new custom(r)).start(); } } class resource{ private int num; boolean flag=false; Lock lock = new ReentrantLock(); Condition pro = lock.newCondition(); Condition con = lock.newCondition(); public void produce(){ try { lock.lock(); if(flag){ pro.await(); } num++; System.out.println(Thread.currentThread().getName() + "生产商品编号" + num + "....."); flag = true; con.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void consume(){ try { lock.lock(); if(!flag){ con.await(); } System.out.println(Thread.currentThread().getName() + "消费商品编号" + num + "....."); flag = false; pro.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } class produce implements Runnable{ private resource r; public produce(resource r) { super(); this.r = r; } public void run() { while (true) { r.produce(); }}} class custom implements Runnable{ private resource r; public custom(resource r) { super(); this.r = r; } public void run() { while (true) { r.consume(); } } }
如何停止线程?
原理:只有一种,run方法结束。
怎样结束run方法呢?
线程运行代码一般都是循环,只要控制住循环,就可以控制run方法,也就是线程结束。
定义循环结束标记,
因为线程运行代码一般都是循环,重要控制了循环即可。
方法一:定义改变标记的方法,在调用者的函数中调用该方法。
特殊方法二:
在while存在wait(),导致当前线程全部冻结。所以没法改变标记,程序无法结束。
可以使用interrupt(),但是它会报异常。所以在异常处理时调用改变标记的方法。
方法二:更专业。
Thread类提供了interrupt方法,
interrupt:清除线程的终结状态。interrupt()方法。
使用interrupt(中断)方法,不是中断,而是让线程回到运行状态。
该方法时结束线程的冻结状态,使线程回到运行状态中来。
注;stop方法已经过时。不在使用。
class stopThread implements Runnable
{ private boolean flag= true;
public synchronized void run(){
while (flag){
try {
wait();
}
catch (InterruptedException e)/InterruptedException的异常
{
System.out.println("InterruptedException run");
changeFlag()//st.changeFlag()也可以结束线程,两者有什么区别//在catch里面处理更专业。
}
System.out.println(Thread.currentThread().getName()+"..run");
}
System.out.println("over");
}
public void changeFlag(){
flag= false;
}
}
特殊情况;
当线程处于冻结状态。就不会读取标记,那么线程就不会结束
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进清除。
强制让线程恢复到运行状态。这样就可以操作标记让线程结束。
Thread类提供了该方法,
interrupt:清除线程的终结状态。interrupt()方法。
使用interrupt(中断)方法。
该方法时结束线程的冻结状态,使线程回到运行状态中来。
注;stop方法已经过时。不在使用。
boolean isDaemon()
测试该线程是否为守护线程。在启动线程前启用
前台线程和后台线程:
前台线程:可以看见的线程,mian方法是前台线程。
后台线程(守护线程):在前台结束后,自动结束。(守护雅典娜,她挂了,升斗士没意义)
守护线程:如果线程被标记为守护线程,它会正常的与前台抢夺cpu资源。区别是:前台线程结束,后台线程
自动结束,虚拟机退出。
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.setDaemon(true);//守护线程,启动前调用
t2.setDaemon(true);
t1.start();
t2.start();
join,当A线程执行到了B线程的.join方法时,A(比如主线程)就会等待。等B线程都执行完后,A才会执行。
用处:join 可以用来临时加入线程执行。
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
// t2.join();//1*不可以放在此处,因为主线程执行到此行,主线程等待,t2还未开始。
t1.join();//主线程冻结并交出执行权。t1结束才会执行。
t2.start();
1.thread.currentThread.toString();获取线程组:
2.优先级:默认优先级5
setPriority(int newPriority) //0-10;
更改线程的优先级。
static int MAX_PRIORITY
线程可以具有的最高优先级。
static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级。
setPriority(10);//别写10,setPriority(Thread.MAX_PRIOITY)
记住:数据固定用常量,数据共享用静态。
static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
先看看该方法的解释,Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程
暂停时什么状态?什么时候解除暂停?
其实yield方法无法实现机会绝对平等。yield()只是使当前线程重新回到【可执行状态】,
允许具有相同优先级的其他线程获得运行机会,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,
即同一线程再次被执行。
一些小问题:当多个循环在一起,只能一个循环结束才能执行下一个,效率低。
show(){
for(int x=0;x++;x<1000){}
for(int x=0;x++;x<1000){}
for(int x=0;x++;x<1000){}
}
怎么解决:封装线程。同匿名内部类,技巧写法。
show(){
new Thread(){
public void run() {
for(int x=0;x++;x<1000){}
}
}.start();
for(int x=0;x++;x<1000){};
Runnable r =new Runnable(){//Thread 继承了Runnalbe
public void run() {
for(int x=0;x++;x<1000){}
} };
new Thread(r).start();
}
也可以用,Excutors线程工具类。
浙公网安备 33010602011771号