title: Day06-线程间通信
date: 2020-06-04 11:03:33
author: 子陌
线程间的通信
线程间通信:多个线程在处理同一资源,但是任务却不同
1 多线程执行同一资源:
// 资源
class Resource {
String name;
String sex;
}
// 输入
class Input implements Runnable {
Resource r;
Input(Resource r) {
this.r = r;
}
public void run() {
boolean b = true;
while (true) {
synchronized (Resource.class) {
if (b) {
r.name = "zimo";
r.sex = "man";
} else {
r.name = "子陌";
r.sex = "女";
}
}
b = !(b);
}
}
}
// 输出
class Output implements Runnable {
Resource r;
Output(Resource r) {
this.r = r;
}
public void run() {
while (true) {
synchronized (Resource.class) {
System.out.println(r.name + ":" + r.sex);
}
}
}
}
class ResourceDemo {
public static void main(String[] args) {
// 创建资源
Resource r = new Resource();
// 创建任务
Input in = new Input(r);
Output out = new Output(r);
// 创建线程
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
// 开启线程
t1.start();
t2.start();
}
}
2 多线程执行同一资源 - 加入等待唤醒功能:
- wait()、notify()、notifyAll(),用来操作线程为什么定义在了Object类中?
- 这些方法存在于同步中
- 使用这些方法时必须要表示所属的同步的锁(对象监视器)
- 锁可以是任意对象,所以任意对象调用的方法一定定义Object类中
- wait()、sleep()有什么区别?
- wait():释放cpu执行权,释放锁
- sleep():释放cpu执行权,不释放锁
/*
* 涉及的方法:
* 1.wait(); 让线程处于冻结状态,被wait的线程会被存储到线程池中。
* 2.notify(); 唤醒线程池中的一个线程(任意的,无序的)。
* 2.notifyAll(); 唤醒线程池中的所有线程。
*/
// 资源
class Resource {
String name;
String sex;
boolean flag = false;
}
// 输入
class Input implements Runnable {
Resource r;
Input(Resource r) {
this.r = r;
}
public void run() {
boolean b = true;
while (true) {
synchronized (Resource.class) {
if(r.flag){
// 如果有内容,那么写线程等待
r.wait();
}else{
// 如果没有内容,就往里写
if (b) {
r.name = "zimo";
r.sex = "man";
} else {
r.name = "子陌";
r.sex = "女";
}
// 标记修改
r.flag = true;
// 唤醒输出线程
r.notify();
b = !(b);
}
}
}
}
}
// 输出
class Output implements Runnable {
Resource r;
Output(Resource r) {
this.r = r;
}
public void run() {
while (true) {
synchronized (Resource.class) {
if(!r.flag){
r.wait();
}else{
System.out.println(r.name + ":" + r.sex);
r.flag = false;
r.notify();
}
}
}
}
}
class ResourceDemo {
public static void main(String[] args) {
// 创建资源
Resource r = new Resource();
// 创建任务
Input in = new Input(r);
Output out = new Output(r);
// 创建线程
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
// 开启线程
t1.start();
t2.start();
}
}
代码优化:
// 资源
class Resource {
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name, String sex){
if(flag){
try{
this.wait();
}catch(InterruptedException e){
}
}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out(){
if(!flag){
try{
this.wait();
}catch(InterruptedException e){
}
}
System.out.println("name :" + name + ",sex :" + sex);
flag = false;
this.notify();
}
}
// 输入
class Input implements Runnable {
Resource r;
Input(Resource r) {
this.r = r;
}
public void run() {
boolean b = true;
while (true) {
if (b) {
r.set("zimo", "man");
} else {
r.set("子陌", "女");
}
b = !(b);
}
}
}
// 输出
class Output implements Runnable {
Resource r;
Output(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.out();
}
}
}
class ResourceDemo {
public static void main(String[] args) {
// 创建资源
Resource r = new Resource();
// 创建任务线程
Thread t1 = new Thread(new Input(r));
Thread t2 = new Thread(new Output(r));
// 开启线程
t1.start();
t2.start();
}
}
3 多生产者-多消费者模式:
// 多生产者-多消费者
// 产品资源
class Resource{
private String name;
private int count;
private boolean flag = false;
public synchronized void set(String name){
while(flag){
try{
this.wait();}catch(InterruptedException e){
}
}
this.name = name + this.count;
count++;
System.out.println(Thread.currentThread().getName() + "..生产者.." + this.name);
flag = true;
notifyAll();
}
public synchronized void out(){
while(!flag){
try{
this.wait();}catch(InterruptedException e){
}
}
System.out.println(Thread.currentThread().getName() + "..消费者.." + this.name);
flag = false;
notifyAll();
}
}
// 生产者
class Producer implements Runnable{
private Resource r;
Producer(Resource r){
this.r = r;
}
public void run(){
while(true){
r.set("烤鸭");
}
}
}
// 消费者
class Consumer implements Runnable{
private Resource r;
Consumer(Resource r){
this.r = r;
}
public void run(){
while(true){
r.out();
}
}
}
class ProducerConsumerDemo{
public static void main(String[] args){
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
- if标记:只有一次判断,会导致不该运行的线程运行了,出现数据错误的情况
- while判断标记:解决了线程重新唤醒执行权后,是否能继续往下运行!如果本方唤醒了本方,没有意义,notify + while 会导致死锁
- notifyAll:解决了,本方线程一定会唤醒对方线程的问题
JDK升级1.5 - 同时更名为5.0版本,同理1.6为6.0
notify()不仅唤醒了对方,同时唤醒了所有人,所以5.0推出java.util.concurrent.licks
同步代码块synchronized,对于锁的操作是隐士的,jdk1.5后将同步锁封装成了对象,并将锁操作的隐式方式定义到了该对象中,将隐式动作变成了显示动作
- Lock替代了Condition方法的使用
- Condition替代了Object监视器方法的使用
class LockDemo{
// 创建一个锁对象
Lock lock = new ReentrantLock();
// 通过已有的锁 获取该锁上的监视器对象
Condition c = lock.newCondition();
//public synchronized void out(){
public void out(){
lock.lock(); /// 获取锁
try{
while(!flag){
//try{this.wait();}catch(InterruptedException e){}
try{
c.await();}catch(InterruptedException e){
}
}
System.out.println(Thread.currentThread().getName() + "..消费者.." + this.name);
flag = false;
c.signalAll();
}finally{
lock.unlock();
}
}
// 通过已有的锁获取两组监视器:一组监视生产者,一组监视消费者
// 生产的时候消费不能动
// 创建一个锁对象
Lock lock = new ReentrantLock();
// 通过已有的锁 获取该锁上的监视器对象
Condition producer_con = lock.newCondition(); // 生产者监视器
Condition consumer_con = lock.newCondition(); // 消费者监视器
public synchronized void set(String name){
while(flag){
try{
producer_con.wait();}catch(InterruptedException e){
}
}
this.name = name + this.count;
count++;
System.out.println(Thread.currentThread().getName() + "..生产者.." + this.name);
flag = true;
consumer_con.signal();
}
public void out(){
lock.lock(); /// 获取锁
try{
while(!flag){
//try{this.wait();}catch(InterruptedException e){}
try{
consumer_con.await();}catch(InterruptedException e){
}
}
System.out.println(Thread.currentThread().getName() + "..消费者.." + this.name);
flag = false;
producer_con.signal();
}finally{
lock.unlock();
}
}
}
- Lock接口:替代了同步代码块或者同步函数。将同步的隐式锁操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器
- lock():获取锁
- unlock():释放锁,通常需要定义fianlly代码块中
- Condition接口:替代了Object中的wait、notify、notifyAll方法,将这些监视器方法单独进行了封装,变成了Condition监视器对象,可任意锁进行组合
- await():等价于wait()
- signal():等价于notify()
- signalAll():等价于notifyAll()
4、wait()和sleep()的区别
-
wait可以指定时间,也可以不指定时间
sleep必须指定时间
-
在同步中时,对于cpu的执行权和锁的处理不同
- wait:释放执行权,释放锁
- sleep:释放执行权,不释放锁
停止线程
- stop()方法
- run()方法结束
1) 定义循环结束标记
- 因为线程运行代码一般都是循环,只要控制了循环即可
class StopThread implements Runnable{
private boolean flag = true;
public void run(){
while(flag){
System.out.println(Thread.currentThread().getName() + ".....");
}
}
public void setFlag(){
this.flag = false;
}
}
class StopThreadDemo{
public static void main(String[] args){
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 1;
for(;;){
if(++num == 50){
st.setFlag();
break;
}
System.out.println("main...." + num);
}
}
}
如果线程处于冻结状态,无法读取标记,如何结束?
2)使用interrupt(中断)方法
- 该方法是结束线程的冻结状态,使线程(强制)回到运行状态中来
- 强制动作会发生InterruptException异常,记得要处理
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
wait(); // 当主线程死亡,子线程没有结束进入等待
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName() + "......" + e);
flag = false;
}
System.out.println(Thread.currentThread().getName() + "*******");
}
}
public void setFlag(){
this.flag = false;
}
}
class StopThreadDemo{
public static void main(String[] args){
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 1;
for(;;){
if(++num == 50){
//st.setFlag(); // 线程进入冻结,无法读取标记
t1.interrupt();
t2.interrupt();
break;
}
System.out.println("main...." + num);
}
System.out.println("main....end");
}
}
stop方法已经过时不再使用
3)守护线程(联合线程/后台线程)
- 如果前台线程都结束了,守护线程(后台线程)无论处于什么状态都将自动结束
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
wait(); // 当主线程死亡,子线程没有结束进入等待
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName() + "......" + e);
flag = false;
}
System.out.println(Thread.currentThread().getName() + "*******");
}
}
public void setFlag(){
this.flag = false;
}
}
class StopThreadDemo{
public static void main(String[] args){
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.setDaemon(true); // 开启t2守护线程(后台线程)
t2.start();
int num = 1;
for(;;){
if(++num == 50){
t1.interrupt();
//t2.interrupt();
break;
}
System.out.println("main...." + num);
}
System.out.println("main....end");
}
}
线程的其他方法
- join()方法:函数执行线程释放执行权和执行资格,等待调用join()方法的线程执行完成
- setPriority()方法:设置线程优先级方法
- yield():暂停一下,释放一下CPU的执行权,同时自己还有机会抢到
class Demo implements Runnable{
public void run(){
for(int i = 0; i < 50; i++){
System.out.println(Thread.currentThread().toString() + "......" + i);
}
}
}
class JoinDemo{
public static void main(String[] args){
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t1.join(); // t1线程要申请加入进来,运行。临时加入一个线程运算时可以使用join()方法
t2.start();
t2.setPriority(Thread.MAX_PRIORITY);
for(int i = 0; i < 50; i++){
System.out.println(Thread.currentThread().getName() + "......" + i);
}
}
}
class ThreadTest{
public static void main(String[] args){
// main
for(int i = 0; i < 50; i++){
System.out.println(Thread.currentThread().getName() + "......" + i);
}
// Thread
new Thread(){
public void run(){
for(int j = 0; j < 50; j++){
System.out.println(Thread.currentThread().getName() + "......" + j);
}
}
}.start();
// Runnable
Runnable r = new Runnable(){
public void run(){
for(int k = 0; k < 50; k++){
System.out.println(Thread.currentThread().getName() + "......" + k);
}
}
}
new Thread(r).start();
}
}
Thread创建线程的时候可以进行组的划分,可以同时进行一整个线程组的操作
面试案例
class Test implements Runnable {
public void run(Thread t){
}
}
// 如果错误,错误发生在哪一行?
/*
* 第一行:抽象类runnable接口没有被覆盖,要么被abstract修饰
* run是子类的Test特有的方法
*/
class ThreadTest {
public static void main(String[] args){
// 如果都没有,以Thread自己为主
new Thread(new Runnable(){
// 没有子类方法,以任务为主
public void run(){
System.out.println("Runnable run");
}
}){
// 子类自己有方法,以子类为主
public void run(){
System.out.println("Thread run");
}
}.start();
}
}