线程间通讯

线程间通讯

多个线程在处理同一个资源,但是任务却不同时,需要使用线程间通讯。

为了实现线程间通信,需要借助Object类提供的wait()、notify()、notifyAll()三个方法。这三个方法不属于Thread,而是Object。这三个方法必须有同步监视器对象调用。
因为监视器对象可以是任意的object对象,任意的对象都能够调用的方法可定得写在object中。

  • 对于synchronized修饰的方法,因为同步监视器默认就是this,所以可以在同步方法中直接调用这三个方法。
  • 对于synchronized修饰的同步代码块。同步器是synchronized后面括号里的对象,所以必须使用括号里的对象调用这三个方法。

这三个方法的作用:

  • wait():是当前的进程等待,直到其他线程调用该同步器的notify()方法或notifyAll()来唤醒线程。调用wait()方法的线程会释放对该同步器的锁定。
  • notify(): 唤醒在此同步器上的等待的单个线程。如果多个线程在同步器上等待,则会随机的唤醒一个。只有当前线程放弃了对同步器的锁定后,才可以执行被唤醒的线程。
  • notifyAll():唤醒在此同步器上的所有线程。只有当前线程放弃了对同步器的锁定后,才可以执行被唤醒的线程。

下面实现了一个生产消费的流程,当生产者生产完成后通知消费者消费,当消费了之后通知生产者接着生产。生产和消费交替进行。

public class ProducerConsumer {

    public static void main(String[] args) {
        Resouse account = new Resouse() ;
        Producer producer = new Producer(account) ;
        Consumer consumer = new Consumer(account);
        //生产线程
        Thread t1 = new Thread(producer);
        //消费线程
        Thread t3 = new Thread(consumer);
        t1.start();
        t3.start();

    }
}

class Resouse {
    //生产的资源,每次生产就加1,消费就输出数值。
    int sum = 0 ;
    //记录生产消费的状态
    Boolean flag = false;
    //生产方法,由于save()和out()方法同时都会操作资源所以加锁
    //根据flag判断状态,如果完成了生产,线程阻塞(通过wait 使线程等待,并释放锁)。当out方法唤醒线程继续向下执行。
    //如果没有完成生产,直接生产,并将状态改为完成生产,唤醒消费线程。
    synchronized void save(){
        if(flag){
            try {
                //线程阻塞
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
            //唤醒在this上等待的方法
            notifyAll();
            flag = true ;
            sum++ ;
            System.out.println("producer"+sum);

    }
    //消费方法:当生产完成后,生产线程会通知消费线程。消费线程获得锁后,进行消费
    //消费完成后给该状态,通知生产进程进行生产。
    synchronized void out(){
        if(!flag){
            try {
                //线程阻塞
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else 
            //唤醒在this上等待的方法
            notifyAll();
            flag = false ;
            System.out.println("consumer<<<"+sum);
    }
}

//生产线程的Target,调用一百次生产方法
class Producer implements Runnable{
    Resouse account ;
    public Producer(Resouse account ){
        this.account = account ;
    }
    public void run(){
        for(int i=0;i<100;i++){
            account.save();
        }
    }
}

//消费线程的Target,调用一百次消费方法。
class Consumer implements Runnable{
    Resouse account ;
    public Consumer(Resouse account ){
        this.account = account ;
    }
    public void run(){
        for(int i=0;i<100;i++){
            account.out();
        }
    }
}

上面的程序使用wait()和notify()进行控制。对于生产线程而言,当程序进入save()方法后,如果flag是true,则表明已经生产了,程序调用wait()方法阻塞线程;否则程序向下执行生产操作,当生产完成了将flag设置成true,再盗用notifyAll()唤醒其他被阻塞的线程。消费线程就开始执行。

使用Conditon控制线程通讯

当程序不实用synchronized关键子来保证同步,而直接使用Lock对像来控制同步。系统中不存在隐式的同步监视器,不能使用wait() 、notify()、notifyAll()方法进行线程通讯。
当使用Lock对象保证线程同步时,可以使用Condition类来进行线程通讯。

Conditon提供可跟上面功能相近的方法

  • await():类是于隐式同步器上的wait()方法,导致当前线程等待,知道其他线程调用该Conditon的signal()方法或是signalAll()方法来唤醒该线程。
  • signal():唤醒在当前Lock上等待的单个线程,如果在Lock对象上有多个线程等待,则会随机的唤醒一个。只有当前线程放弃了对Lock对象的锁定后,才能执行被唤醒的方法。
  • signalAll():唤醒在当前Lock上等待的所有线程。只有当前线程放弃了对Lock对象的锁定后,才能执行被唤醒的方法。

        public class ProducerConsumer {
    
            public static void main(String[] args) {
                Resouse account = new Resouse() ;
                Producer producer = new Producer(account) ;
                Consumer consumer = new Consumer(account);
                //生产线程
                Thread t1 = new Thread(producer);
                Thread t2 = new Thread(producer);
                Thread t3 = new Thread(producer);
                //消费线程
                Thread t4 = new Thread(consumer);
                t1.start();
                t3.start();
                t2.start();
                t4.start();
    
            }
        }
    
        class Resouse {
            //new 一个Lock的实例
            Lock lock = new ReentrantLock();
            //同过lock获得Conditon
            Condition conditon = lock.newCondition();
    
            Condition con_producer = lock.newCondition();
            Condition con_consuner = lock.newCondition();
    
            int sum = 0 ;
    
            Boolean flag = false;
    
            public void save(){
                lock.lock();
                try{
                    while(flag){
                        try {
                            con_producer.await();
                        } catch (InterruptedException e) {
    
                            e.printStackTrace();
                        }
                    }
                        con_consuner.signal();
                        flag = true ;
                        sum++ ;
                        System.out.println("producer"+sum);
                }finally{
                    lock.unlock();
                }
    
    
            }
    
    
            public void out(){
                lock.lock();
                try{
                    while(!flag){
                        try {
                            con_consuner.await();
                        } catch (InterruptedException e) {
    
                            e.printStackTrace();
                        }
                    }
                        con_producer.signal();
                        flag = false ;
                        System.out.println("consumer<<<"+sum);
                }finally{
                    lock.unlock();
                }
            }
        }
    
    
        class Producer implements Runnable{
            Resouse account ;
            public Producer(Resouse account ){
                this.account = account ;
            }
            public void run(){
                for(int i=0;i<100;i++){
                    account.save();
                }
            }
        }
    
    
        class Consumer implements Runnable{
            Resouse account ;
            public Consumer(Resouse account ){
                this.account = account ;
            }
            public void run(){
                for(int i=0;i<100;i++){
                    account.out();
                }
            }
        }
    

停止线程

当线程任务中的循环结束的时候,线程就停止了。只要控制循环就可以结束线程。
控制循环通常用定义标记来完成。

    public class StopThread implements Runnable{
        Boolean flag = true ;
        public static void main(String args []){
            StopThread s = new StopThread();
            Thread t = new Thread(s) ;
            Thread t2 = new Thread(s) ;
            t.start();
            t2.start();

            //主线程sleep 1秒后 设置 flag为false。子线程停止
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            s.setFlag();

        }

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

        void setFlag(){
            flag = false ;
        }

    }

如果线程处于冻结状态,无法读取标记如何结束

可使用interrupt()方法将处于冻结状态的线程强行恢复过来。强行打断会发生InterruptedException,处理Excepiton是将flag设置为false。

public class StopThread implements Runnable{
    Boolean flag = true ;
    public static void main(String args []){
        StopThread s = new StopThread();
        Thread t = new Thread(s) ;
        t.start();


        //主线程sleep 1秒后 打断线程t。子线程停止
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t.interrupt();
    }

    public synchronized void run(){
        while(flag){
            try {
                wait();
            } catch (InterruptedException e) {
                //处理异常,将flag设置成false
                flag = false ;
                e.printStackTrace();
            }

        }
        System.out.println(Thread.currentThread().getName()+"over");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值