黑马程序员---线程间通讯
2015-09-29 15:21
232 查看
---------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
关键字:等待唤醒机制、Lock、Condition、停止线程、守护线程、join方法、优先级、yeild
线程间的通信
之前的例子,卖票和存钱,不同线程运行的代码是一致的。线程间的通信解决的是,多个线程操作同一个资源,但是操作动作不同。所以需要多个run方法来存放数据。
处理同一个资源的所有语句都需要同步,即使不在同一个类里或只有一句代码,多个线程意味着多个run方法,每个 run方法里都有同步才行。
对于共享数据的操作可能有先后顺序,例如,输入、输出。但是具体执行时,可能是输入一段、输出一段,不能做到交替进行。可以通过在共享数据内加入判断语句来交替进行。
等待唤醒机制
线程运行时,内存中会建立“线程池”,等待线程都存在线程池中。等待唤醒机制使得多个线程可以交替进行。
wait(),等待中,让线程进入线程池,但是执行资格仍在。且不能自己醒。会跑出异常,需要在函数内部处理。
notify唤醒的是线程池中的线程,通常唤醒第一个等待的线程。notifyAll();全部唤醒。
wait、notify、notifyaAll
都使用在同步中。因为要对持有锁的线程操作,所以要使用在同步中,因为同步才有锁。也就是说,这些方法必须标示所属线程使用的锁,例如r.wait(),意为持有r这个锁的线程等待。因为同步会出现嵌套,会有2个锁,不标示的话,不知道让哪个线程等待。同理适用于notify。
这些方法定义在Object类中的原因。因为这些方法在操作同步中的线程时,都必须要标识他们所操作线程的唯一锁。只有同一个锁上被等待线程,可以被同一个锁上另外的线程notify唤醒。也就是说,等待和唤醒必须是同一个锁的2个线程之间的行为。
因为锁可以是任意对象,所以这些方法可以被任意对象调用,所以这些方法都定义在了Object类中。
多个线程操作同一个数据,但是动作不同,可以使用等待唤醒机制,交替执行。例如,
public synchronized void set(String name)//保证线程会执行完代码后才放弃资格。
{
while(flag)//循环判断,使得线程不会轻易执行。
try{this.wait();}catch(Exception e){}
this.name = name +"---"+ count++;
System.out.println(Thread.currentThread().getName()+"---生产者"+this.name);
flag = true;//实例变量,会记录数据。
this.notifyAll();//前面都判断的话,可能会都等待,所以需要唤醒对象线程。因为循环判断,所以不用担心一起执行。
}
public synchronized void out()
{
while(!flag)//线程都在此处等待,唤醒后直接执行。需要使线程再次判断,所以用while。
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---消费者--"+this.name);
flag = false;
this.notifyAll();
}
关键点是循环判断while(flag)和唤醒对象线程this.notifyAll();,主要是考虑线程较多,需要挨个交替执行。
JDK1.5提供了多线程升级解决方案,Lock接口和Condition接口。将同步synchronized替换为显式Lock操作(显式的锁机制);将Object中的wait、notify、notifyAll替换为Condition对象(显式的等待、唤醒操作机制),该对象可以对Lock锁进行获取。synchronized中对锁的操作,例如,获取锁、获取锁,都是隐式操作。lock没有新的功能,只是将对锁的操作公开化、显式,获取锁lock()和释放所unlock()。将等待、唤醒操作封装进Condition对象,使得一个锁可以对应多个对象。例如。
private Lock lock = new ReentrantLock();//新建锁的对象
private Condition condition_pro = lock.newCondition();//新建对象,封装锁的使用。
private Condition condition_con = lock.newCondition();
public void set(String name) throws InterruptedException
{
lock.lock();
try
{
while(flag)
condition_pro.await();
this.name = name +"---"+ count++;
System.out.println(Thread.currentThread().getName()+"---生产者"+this.name);
flag = true;
condition_con.signal();//只能唤醒condition_con,与下面对应。
}
finally
{
lock.lock();//释放锁的动作一定要执行。
}
}
public void out() throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();//只能让condition_con等待,与前面对应
System.out.println(Thread.currentThread().getName()+"---消费者---"+this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.lock();
}
}
优势在于,一个锁里可以绑定多个Condition对象。以前同步里面多个线程拥有一个锁,绑定一个对象,等待唤醒操作也只能由这个对象决定;现在锁可以绑定多个对象,进而有多个等待唤醒操作,操作更加灵活,只需要区分Condition对象即可。最大的优点是可以做到,本方只唤醒对方的操作。
可以在嵌套中使用,不会出现死锁的情况。
锁需要绑定一个对象,这个对象可以是任意对象,可以和锁没有关系。现在,一个锁可以和多个对象绑定,也就意味着,一些线程和一个Condition产生联系,线程的等待唤醒由这个对象决定,例如,condition_pro.await();,condition_pro对象使得线程等待,那么无论在任何地方,只要condition_pro.signal()就能唤醒线程,一般都是唤醒对方的线程。
停止线程
原理:run方法结束。注意:是停止,不是暂停(冻结)。
1. 定义循环结束标记
多线程运行,代码一般都是循环结构,只要控制循环,就可以让run方法结束,也就是线程结束。一次性执行的话,单线程即可。
2. 使用interrupt(中断)方法
结束线程的冻结状态,使线程回到运行状态中。
还有stop方法,但1.5版本后不再使用,会报错。但是老版本有此方法。在线程还处于冻结状态,就将其终止,有安全隐患。
特殊情况
当现场处于冻结状态,就不会读取到标记,现场就不会结束,只能是暂停,只是丧失了运行资格而已。
中断状态不是停止状态,是冻结状态或暂停状态。interrupt方法,将处于冻结状态的线程强制的恢复到运行状态中来。只有恢复到运行状态,才能读取标记,使得run方法结束。
当没有指定方式让冻结的线程恢复到冻结状态时,需要对冻结状态进行清除,强制让线程恢复到运行状态中来。这样就可以通过操作标记让线程结束,线程运行完毕后自动结束。Thread类中提供了interrupt方法,满足该需求。
守护线程
守护线程,也叫用户线程,本质是后台线程。大部分线程都是前台线程。标为后台线程后,开启后和前台线程一样,抢劫CPU的运行权;当所有的前台线程都结束后,后台线程会自动结束,甚至无限循环也会结束。
特点:
1. 必须在守护线程前调用。
2. 所有前台线程都结束,只剩下守护线程时,守护线程结束,虚拟机退出,程序结束。
join方法
join方法,抢先获得CPU的执行权,先于主线程执行,例如,t1.join();。此时,主线程处于冻结状态,只有t1可以运行;只有t1运行结束,主线程才可以运行,等待让主线程让出执行权的线程死亡。一般用于临时加入线程。
join方法的位置会有影响,如果放在多个线程开启的下面,例如,t1.start();t2.start();t1.join();,t1和t2会交替执行,因为t1抢的是主线程的运行权,和t2无关。主线程依然在t1结束后运行,和t2的执行无关。
当A现场执行到了B线程的join方法时,A线程就会等待,等B线程都执行完,A线程才会执行。可以用来临时加入线程执行。可以嵌套使用。
如果t1等待,会造成主线程无法运行,所以使用interrupt方法来结束冻结状态,继续运行。因为是强制恢复运行,所以可能会出现异常,所以join方法会抛出InterruptedException异常。
开启线程的线程组,称为主线程组,里面的成员构成一个线程组。也可以通过ThreadGroup创建新的对象,封装进其他的组。很少用。
优先级:代表抢资源的频率,设置越高,执行越频繁,优先执行。所有线程的优先级,包括主线程,优先级默认设置为5,共有10级,数字越大,越优先执行。如果数字相差不大,优先程度几乎没有差别,只有1、5、10之间最明显,分别为min、norm、max。虽然可以设为最高级,也只是相对高而已,不可能只执行一个线程。
yield
多个线程运行时,临时强制释放线程的执行权,使得其他线程可以执行。可以减缓线程的运行,使得线程都有机会运行。
开发时如何编写线程
当某些代码需要同时运行时,用单独的线程进行封装。可以封装成类,或者对象。例如。
new Thread()//通过Thread类建立匿名类
{
public void run()
{
for (int x=0;x<30;x++ )
{
System.out.println(Thread.currentThread().getName()+"...Jobs..."+x);
}
}
}.start();
Runnable r = new Runnable()//通过Runnable接口建立匿名类对象
{
public void run()
{
for (int x=0;x<30;x++ )
{
System.out.println(Thread.currentThread().getName()+"...Intel..."+x);
}
}
};
new Thread(r).start();
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
关键字:等待唤醒机制、Lock、Condition、停止线程、守护线程、join方法、优先级、yeild
线程间的通信
之前的例子,卖票和存钱,不同线程运行的代码是一致的。线程间的通信解决的是,多个线程操作同一个资源,但是操作动作不同。所以需要多个run方法来存放数据。
处理同一个资源的所有语句都需要同步,即使不在同一个类里或只有一句代码,多个线程意味着多个run方法,每个 run方法里都有同步才行。
对于共享数据的操作可能有先后顺序,例如,输入、输出。但是具体执行时,可能是输入一段、输出一段,不能做到交替进行。可以通过在共享数据内加入判断语句来交替进行。
等待唤醒机制
线程运行时,内存中会建立“线程池”,等待线程都存在线程池中。等待唤醒机制使得多个线程可以交替进行。
wait(),等待中,让线程进入线程池,但是执行资格仍在。且不能自己醒。会跑出异常,需要在函数内部处理。
notify唤醒的是线程池中的线程,通常唤醒第一个等待的线程。notifyAll();全部唤醒。
wait、notify、notifyaAll
都使用在同步中。因为要对持有锁的线程操作,所以要使用在同步中,因为同步才有锁。也就是说,这些方法必须标示所属线程使用的锁,例如r.wait(),意为持有r这个锁的线程等待。因为同步会出现嵌套,会有2个锁,不标示的话,不知道让哪个线程等待。同理适用于notify。
这些方法定义在Object类中的原因。因为这些方法在操作同步中的线程时,都必须要标识他们所操作线程的唯一锁。只有同一个锁上被等待线程,可以被同一个锁上另外的线程notify唤醒。也就是说,等待和唤醒必须是同一个锁的2个线程之间的行为。
因为锁可以是任意对象,所以这些方法可以被任意对象调用,所以这些方法都定义在了Object类中。
多个线程操作同一个数据,但是动作不同,可以使用等待唤醒机制,交替执行。例如,
public synchronized void set(String name)//保证线程会执行完代码后才放弃资格。
{
while(flag)//循环判断,使得线程不会轻易执行。
try{this.wait();}catch(Exception e){}
this.name = name +"---"+ count++;
System.out.println(Thread.currentThread().getName()+"---生产者"+this.name);
flag = true;//实例变量,会记录数据。
this.notifyAll();//前面都判断的话,可能会都等待,所以需要唤醒对象线程。因为循环判断,所以不用担心一起执行。
}
public synchronized void out()
{
while(!flag)//线程都在此处等待,唤醒后直接执行。需要使线程再次判断,所以用while。
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---消费者--"+this.name);
flag = false;
this.notifyAll();
}
关键点是循环判断while(flag)和唤醒对象线程this.notifyAll();,主要是考虑线程较多,需要挨个交替执行。
JDK1.5提供了多线程升级解决方案,Lock接口和Condition接口。将同步synchronized替换为显式Lock操作(显式的锁机制);将Object中的wait、notify、notifyAll替换为Condition对象(显式的等待、唤醒操作机制),该对象可以对Lock锁进行获取。synchronized中对锁的操作,例如,获取锁、获取锁,都是隐式操作。lock没有新的功能,只是将对锁的操作公开化、显式,获取锁lock()和释放所unlock()。将等待、唤醒操作封装进Condition对象,使得一个锁可以对应多个对象。例如。
private Lock lock = new ReentrantLock();//新建锁的对象
private Condition condition_pro = lock.newCondition();//新建对象,封装锁的使用。
private Condition condition_con = lock.newCondition();
public void set(String name) throws InterruptedException
{
lock.lock();
try
{
while(flag)
condition_pro.await();
this.name = name +"---"+ count++;
System.out.println(Thread.currentThread().getName()+"---生产者"+this.name);
flag = true;
condition_con.signal();//只能唤醒condition_con,与下面对应。
}
finally
{
lock.lock();//释放锁的动作一定要执行。
}
}
public void out() throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();//只能让condition_con等待,与前面对应
System.out.println(Thread.currentThread().getName()+"---消费者---"+this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.lock();
}
}
优势在于,一个锁里可以绑定多个Condition对象。以前同步里面多个线程拥有一个锁,绑定一个对象,等待唤醒操作也只能由这个对象决定;现在锁可以绑定多个对象,进而有多个等待唤醒操作,操作更加灵活,只需要区分Condition对象即可。最大的优点是可以做到,本方只唤醒对方的操作。
可以在嵌套中使用,不会出现死锁的情况。
锁需要绑定一个对象,这个对象可以是任意对象,可以和锁没有关系。现在,一个锁可以和多个对象绑定,也就意味着,一些线程和一个Condition产生联系,线程的等待唤醒由这个对象决定,例如,condition_pro.await();,condition_pro对象使得线程等待,那么无论在任何地方,只要condition_pro.signal()就能唤醒线程,一般都是唤醒对方的线程。
停止线程
原理:run方法结束。注意:是停止,不是暂停(冻结)。
1. 定义循环结束标记
多线程运行,代码一般都是循环结构,只要控制循环,就可以让run方法结束,也就是线程结束。一次性执行的话,单线程即可。
2. 使用interrupt(中断)方法
结束线程的冻结状态,使线程回到运行状态中。
还有stop方法,但1.5版本后不再使用,会报错。但是老版本有此方法。在线程还处于冻结状态,就将其终止,有安全隐患。
特殊情况
当现场处于冻结状态,就不会读取到标记,现场就不会结束,只能是暂停,只是丧失了运行资格而已。
中断状态不是停止状态,是冻结状态或暂停状态。interrupt方法,将处于冻结状态的线程强制的恢复到运行状态中来。只有恢复到运行状态,才能读取标记,使得run方法结束。
当没有指定方式让冻结的线程恢复到冻结状态时,需要对冻结状态进行清除,强制让线程恢复到运行状态中来。这样就可以通过操作标记让线程结束,线程运行完毕后自动结束。Thread类中提供了interrupt方法,满足该需求。
守护线程
守护线程,也叫用户线程,本质是后台线程。大部分线程都是前台线程。标为后台线程后,开启后和前台线程一样,抢劫CPU的运行权;当所有的前台线程都结束后,后台线程会自动结束,甚至无限循环也会结束。
特点:
1. 必须在守护线程前调用。
2. 所有前台线程都结束,只剩下守护线程时,守护线程结束,虚拟机退出,程序结束。
join方法
join方法,抢先获得CPU的执行权,先于主线程执行,例如,t1.join();。此时,主线程处于冻结状态,只有t1可以运行;只有t1运行结束,主线程才可以运行,等待让主线程让出执行权的线程死亡。一般用于临时加入线程。
join方法的位置会有影响,如果放在多个线程开启的下面,例如,t1.start();t2.start();t1.join();,t1和t2会交替执行,因为t1抢的是主线程的运行权,和t2无关。主线程依然在t1结束后运行,和t2的执行无关。
当A现场执行到了B线程的join方法时,A线程就会等待,等B线程都执行完,A线程才会执行。可以用来临时加入线程执行。可以嵌套使用。
如果t1等待,会造成主线程无法运行,所以使用interrupt方法来结束冻结状态,继续运行。因为是强制恢复运行,所以可能会出现异常,所以join方法会抛出InterruptedException异常。
开启线程的线程组,称为主线程组,里面的成员构成一个线程组。也可以通过ThreadGroup创建新的对象,封装进其他的组。很少用。
优先级:代表抢资源的频率,设置越高,执行越频繁,优先执行。所有线程的优先级,包括主线程,优先级默认设置为5,共有10级,数字越大,越优先执行。如果数字相差不大,优先程度几乎没有差别,只有1、5、10之间最明显,分别为min、norm、max。虽然可以设为最高级,也只是相对高而已,不可能只执行一个线程。
yield
多个线程运行时,临时强制释放线程的执行权,使得其他线程可以执行。可以减缓线程的运行,使得线程都有机会运行。
开发时如何编写线程
当某些代码需要同时运行时,用单独的线程进行封装。可以封装成类,或者对象。例如。
new Thread()//通过Thread类建立匿名类
{
public void run()
{
for (int x=0;x<30;x++ )
{
System.out.println(Thread.currentThread().getName()+"...Jobs..."+x);
}
}
}.start();
Runnable r = new Runnable()//通过Runnable接口建立匿名类对象
{
public void run()
{
for (int x=0;x<30;x++ )
{
System.out.println(Thread.currentThread().getName()+"...Intel..."+x);
}
}
};
new Thread(r).start();
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
相关文章推荐
- 黑马程序员--泛型
- 黑马程序员---Map集合
- 黑马程序员---字符流,字节流
- 黑马程序员--正则表达式
- 黑马程序员---Elicpse
- 黑马程序员----枚举反射
- 黑马程序员------内省
- Android面试题目汇总
- 职业发展
- 面试题搜集的比较好的链接
- Java程序员们最常犯的3个集合错误
- 穷其一生,做不完一场梦
- 网易校招前端面试
- 黑马程序员----C 语言学习笔记之结构体
- 黑马程序员——java开发前奏
- 阿里实习笔试面试
- 黑马程序员—正则表达式+反射
- 黑马程序员—多线程
- 黑马程序员—异常+IO流File类+Properties+PrinWriter+合并流+切割文件+对象序列化+管道流+RandomAccessFile+操作基本数据类型的DataStre
- 野生程序员的故事