java毕向东学习笔记——day12

一 多线程通讯

  1. 多线程通讯的意思其实很简单,就是不同于以往的两个线程做同样的事情操作同一个对象——比如day11的火车售票,而是多个线程执行对于同一个对象执行不同的事情,比如说今天的对同一个对象中的数据,一个线程负责输入,一个线程负责输出;
  2. 多线程通讯由于不同线程做的事情不同,因此需要多个对象实现Runnable接口并重写run方法;
  3. 多线程应当注意的仍然是安全问题,因此在需要同步的地方慎重判断,本例由于操作的还是同一个对象中的数据,因此两个run方法所在的Input和Output的同步函数可以用操作对象当做锁,从而实现同一把锁,避免安全隐患;
  4. 今天的代码中要实现的是输入数据,而后立刻输出,因此引入了flag进行判断输入的数据是否已经输出,以及wait方法和notify方法用于暂停/唤醒——输入/输出线程;
  5. 如果数据已经输出则输入数据,如果数据未输出则暂停当前的输入线程,唤醒输出线程,转到输出线程进行输出,在输出线程中判断数据是否已经输出,如果没有,则输出数据,如果已经输出,则暂停输出线程,唤醒输入线程进行输入;
  6. 以上的话过于繁琐,还是看两个版本的代码来的快。。。
/*
    输入一个姓名和性别,然后用另一个线程输出
*/

class Resource{
    String name;
    String sex;
    boolean flag = false; //用来作为判定输入是否输出的标识
}

class Input implements Runnable{
    private Resource res;
    Input(Resource res){
        this.res = res;
    }
    public void run(){
        int x = 0;
        while(true){
            //两个线程用同一个对象锁
            synchronized(res){
                if (res.flag)
                    try{
                        res.wait();
                    }catch(InterruptedException e){

                    }
                if(x==0){
                    res.name = "张三";
                    res.sex = "男";
                }else {
                    res.name = "WWW";
                    res.sex = "woman";
                }
                x = (x+1)%2;
                res.flag = true;
                res.notify();
            }   
        }
    }
}

class Output implements Runnable{
    private Resource res;
    Output(Resource res){
        this.res = res;
    }
    public void run(){
        while(true){
            //两个线程用同一个对象锁
            synchronized(res){
                if(!res.flag)
                    try{
                        res.wait();
                    }catch(InterruptedException e){

                    }
                System.out.println(res.name+"......"+res.sex);
                res.flag = false;
                res.notify();
            }
        }
    }
}

class ThreadCommunicationDemo{
    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();
    }
}

下面的是优化版本

/**
    用一个线程输入姓名和性别,用另一个线程输出姓名和性别的
    优化版本!
    @author zck;
    @version V1.1;
*/

class Resource{
    private String name;
    private String sex;
    private boolean flag = false; //用来作为判定输入是否已经输出的标识

    public synchronized void setNameSex(String name,String sex){
        if (flag){
            try{
                this.wait();
            }catch(InterruptedException e){

            }
        }   
        this.name = name;
        this.sex = sex;
        flag = true;
        notify();
    }

    public synchronized void getNameSex(){
        if (!flag){
            try{
                this.wait();
            }catch(InterruptedException e){

            }
        }
        System.out.println(this.name+"......"+this.sex);
        flag = false;
        notify();
    }
}

class Input implements Runnable{
    private Resource res;
    Input(Resource res){
        this.res = res;
    }
    public void run(){
        int x = 0;
        while(true){
                if(x==0){
                    res.setNameSex("张三","男");
                }else {
                    res.setNameSex("WWW","woman");
                }
                x = (x+1)%2;
        }   
    }
}


class Output implements Runnable{
    private Resource res;
    Output(Resource res){
        this.res = res;
    }
    public void run(){
        while(true){
                res.getNameSex();
        }
    }
}


class ThreadCommunicationDemo2{                //优化版本
    public static void main(String[] args){
        Resource r = new Resource();
        new Thread(new Input(r)).start();
        new Thread(new Output(r)).start();
    }
}

二 多线程通讯——生产者消费者

  1. 在多个线程运行时,有多个生产者,多个消费者;
  2. 在屏幕上打印输出时,有时候会出现生产两次却只消费了一次的情况,这与预期的生产一次消费一次不符合,这种安全隐患来自于flag的if判断,因为只判定一次,当生产线程1通过if判断时,执行完wait语句就失去了执行权,然后接下来消费线程1执行,然后生产线程2执行(因为此时flag是false),当生产线程2执行完时,flag变成了true,但是由于生产线程1已经在之前中通过了if判断,当生产线程1获得执行权时,就会再生产一次;
    打印结果:
    生产商品1
    消费商品1
    生产商品2
    生产商品3
    消费商品3
    改进方法就是将if换成while判断

  3. 还有一种安全隐患就是notify叫醒的线程池里本类线程,就会出现叫醒生产线程1叫醒生产线程2,然后所有线程都挂了(都进入wait状态);处理方式就是改notify为notifyAll,干脆全叫醒,就不会出现所有线程一起挂在那儿的情况了;

class ProducerConsumerDemo{
    public static void main(String[] args){
        ProducerConsumer pc = new ProducerConsumer();

        Producer pro = new Producer(pc);
        Consumer con = new Consumer(pc);

        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();
    }
}

class ProducerConsumer{
    private String name;
    private int count = 1;
    private boolean flag = false;

    public synchronized void setName(String name){
        while(true){
            while(flag){
                try{
                    wait();
                }catch(InterruptedException e){

                }
            }
            this.name = name+"..."+count++;
            System.out.println(Thread.currentThread().getName()+"---生产---"+this.name);
            flag = true;
            this.notifyAll();
        }   
    }

    public synchronized void getName(){
        while(true){    
            while(!flag){
                try{
                    wait();
                }catch(InterruptedException e){

                }
            }
            System.out.println(Thread.currentThread().getName()+"---消费-----"+this.name);
            flag = false;
            this.notifyAll();
        }   
    }
}

class Producer implements Runnable{
    ProducerConsumer pc;
    Producer(ProducerConsumer pc){
        this.pc = pc;
    }
    public void run(){
        pc.setName("商品");
    }
}

class Consumer implements Runnable{
    ProducerConsumer pc;
    Consumer(ProducerConsumer pc){
        this.pc = pc;
    }
    public void run(){
        pc.getName();
    }
}

下面是java5.0改进版本
1. 引入了lock代替synchronized,condition对象的await代替wait,signal和signalAll代替notify和notifyAll;

import java.util.concurrent.locks.*;

class ProducerConsumerDemo2{
    public static void main(String[] args){
        ProducerConsumer pc = new ProducerConsumer();

        Producer pro = new Producer(pc);
        Consumer con = new Consumer(pc);

        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();
    }
}

class ProducerConsumer{
    private String name;
    private int count = 1;
    private boolean flag = false;

    private Lock lock = new ReentrantLock();
    private Condition condition_pro = lock.newCondition();
    private Condition condition_con = lock.newCondition();

    public void setName(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.signalAll();
        }finally{       
            lock.unlock();
        }   
    }

    public void getName() throws InterruptedException{  
        lock.lock();
        try{
            while(!flag){
                condition_con.await();
            }
            System.out.println(Thread.currentThread().getName()+"---消费-----"+this.name);
            flag = false;
            condition_pro.signalAll();
        }finally{
            lock.unlock();
        }   
    }
}

class Producer implements Runnable{
    ProducerConsumer pc;
    Producer(ProducerConsumer pc){
        this.pc = pc;
    }
    public void run(){
        while(true){
            try{
                pc.setName("商品");
            }catch(InterruptedException e){

            }
        }   
    }
}

class Consumer implements Runnable{
    ProducerConsumer pc;
    Consumer(ProducerConsumer pc){
        this.pc = pc;
    }
    public void run(){
        while(true){
            try{
                pc.getName();
            }catch(InterruptedException e){

            }   
        }   
    }
}

三 中断线程

  1. 有时候线程处于冻结状态后无法正常读取唤醒标记,这时候就需要外力强制中断线程的冻结状态;
  2. Thread类就提供了interrupt方法,强制唤醒线程(好比把被催眠的人一砖头砸醒),但是这样会导致wait或者sleep抛出异常(因为被砖头砸醒不比正常唤醒,会受伤的,,);
class InterruptThreadDemo{
    public static void main(String[] args){
        int num = 0;

        TestThread tt = new TestThread();
        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);

        t1.start();
        t2.start();

        while(true){
            if(num++ == 60){
                //tt.changeFlag();
                //强制唤醒了t1和t2线程
                t1.interrupt();   
                t2.interrupt();
                break;
            }
            System.out.println(Thread.currentThread().getName()+"..."+num);
        }
    }
}

class TestThread implements Runnable{
    private boolean flag = true;

    public synchronized void run(){
        while(flag){
            try{
                wait();
            }catch(InterruptedException e){
                System.out.println(Thread.currentThread().getName()+"InterruptedException");
                //结束循环实则为结束进程
                flag = false;
            }
            System.out.println(Thread.currentThread().getName()+"run.....");
        }
    }

    public void changeFlag(){
        flag = false;
    }
}

四 守护线程

  1. 可以通过Thread 的setDaemon方法将一些线程设置为守护线程;
  2. 守护线程的设置应当在线程执行前;
  3. 守护线程可视为后台线程,当所有前台线程都结束时,守护线程自动结束;
  4. 这意味着守护线程即使被wait了,也没必要唤醒后再结束,只要前台线程跑完即可;
class InterruptThreadDemo{
    public static void main(String[] args){
        int num = 0;

        TestThread tt = new TestThread();
        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);

        t1.setDaemon(true);
        t2.setDaemon(true);
        t1.start();
        t2.start();

        while(true){
            if(num++ == 60){
                //tt.changeFlag();
                //强制唤醒了t1和t2线程
                //t1.interrupt();   
                //t2.interrupt();
                break;
            }
            System.out.println(Thread.currentThread().getName()+"..."+num);
        }
    }
}

class TestThread implements Runnable{
    private boolean flag = true;

    public void run(){
        while(flag){
            System.out.println(Thread.currentThread().getName()+"run.....");
        }
    }

    public void changeFlag(){
        flag = false;
    }
}

五 join、yield方法和优先级

  1. join方法实则为暂停当前线程,优先执行调用join方法的线程,直到调用join方法的线程结束后再执行当前线程;
  2. toString方法可以查看线程名称,优先级,以及所在线程组;
  3. 优先级为1——10,代表被cpu执行的概率;
  4. 通过setPriority(Thread.MAX/MIN/NORM_PRIORITY)设置三个不同的优先级,对应10/1/5;
  5. yield方法是暂停当前线程,执行其他线程;
class JoinDemo{
    public static void main(String[] args) throws InterruptedException{
        Join jo = new Join();

        Thread t1 = new Thread(jo);
        Thread t2 = new Thread(jo);

        t1.start();
        t1.setPriority(Thread.MAX_PRIORITY);
        t2.start();
        //t1.join();
        for(int x = 0;x<80;x++){
            System.out.println(Thread.currentThread().toString()+"....."+x);
        }
    }
}

class Join implements Runnable{
    public void run(){
        for (int x = 0;x<70;x++){
            System.out.println(Thread.currentThread().toString()+"..."+x);
            //Thread.yield();
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值