Java多线程--线程的同步与通信

线程的同步与通信

一、线程的同步
上一篇手记《多线程的创建与使用》中有个练习题:
模拟火车站售票窗口,开启三个窗口售票,总票数为100
当时我没有考虑线程安全问题,今天我再把它拿出来,用线程的同步机制来实现线程的安全。
在之前那段程序中存在线程安全问题,打印车票时可能出现重复车票以及错票。
那么线程安全问题存在的原因?
由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在安全问题。
2、如何解决线程的安全问题?
必须让一个线程操作共享数据完毕以后,其他线程才有机会参与共享数据的操作
3、java如何实现线程的安全:线程的同步机制
synchronized是java中的一个关键字,也就是说是Java语言内置的特性。
(当然还有其他方式,我就只对synchronized展开来讲)
方式一:同步代码块
synchronized(需要一个任意的对象(锁)){
//需要被同步的代码块(即为操作共享数据的代码)
}
(1)共享数据:多个线程共同操作的同一个数据(变量)
(2)同步监视器:由一个类的对象来充当,哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁。
要求:所有线程必须共用同一把锁。
注:在实现的方式中,考虑同步的话,可以使用this来充当锁,但在继承的方式中,慎用。
实例:模拟火车站售票窗口,开启三个窗口售票,总票数为100

class Window2 implements Runnable{
   int ticket=100;//共享数据
   //Object obj=new Object();
    public void run() {
        while(true){
            synchronized(this){//this表示当前对象,即为w,只有一个
            if(ticket>0){
                System.out.println(Thread.currentThread().getName()+"售出的票号为:"+ticket--);
            }
          }
        }

    }

}
public class TestWindow2 {

    public static void main(String[] args) {
        Window2 w=new Window2();
        Thread t1=new Thread(w);
        Thread t2=new Thread(w);
        Thread t3=new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}


方式二:同步方法
将操作共享数据的方法声明为synchronized,即此方法为同步方法,能够保证当其中一个线程执行
此方法时,其他线程在外等待直至此线程执行完此方法。
同步方法的锁:this
实例:模拟火车站售票窗口,开启三个窗口售票,总票数为100

class Window3 extends Thread{
     static int ticket=100;//共享数据
    Object obj=new Object();
    public void run(){
        while(true){
            show();
        }
    }
    public synchronized void show(){

            if(ticket>0){
                System.out.println(Thread.currentThread().getName()+"售出的票号为:"+ticket--);
            }
    }

}

public  class TestWindow3 {
    public static void main(String[] args) {
        Window3 w1=new Window3();
        Window3 w2=new Window3();
        Window3 w3=new Window3();

        w1.setName("窗口1");
        w2.setName("窗口2");
        w3.setName("窗口3");

        w1.start();
        w2.start();
        w3.start();
    }

}

4、由于同一个时间只能有一个线程访问共享数据,效率变低了
5、释放锁的操作:
(1)当前线程的同步方法、同步代码块执行结束;
(2)当前线程的同步方法、同步代码块中遇到break、return终止了该方法、该代码块的继续执行;
(3)当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束;
(4)当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停并释放锁。
6、不会释放锁的操作:线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂时停止当前线程的执行。

二、线程的通信

1、wait()与notify()和notifyAll()
(1)wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问;
(2)notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待;
(3)notifyAll():唤醒正在排队等待的所有线程结束等待。
这三个方法只有在synchronized方法或synchronized代码块中才能使用。
实例:

package com.TestThread;
//线程通信
//使用两个线程打印1-100,线程1,线程2交替打印
class PrintNum implements Runnable{
    int num=1;
    public void run() {
        while(true){
            synchronized (this) {
                notify();
                if (num <= 100) {
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    num++;
                } else {
                    break;
                }

                try {
                    wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}
public class TestCommunication {
    public static void main(String[] args) {
        PrintNum p=new PrintNum();
        Thread t1=new Thread(p);
        Thread t2=new Thread(p);

        t1.setName("线程1");
        t2.setName("线程2");

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


三、练习
生产者消费者问题
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,
店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,
如果店中有空位放产品了再通知生产者继续生产,如果店中没有产品,店员会告诉消费者等一下,
如果店中有产品了再通知消费者来取走产品。
分析:

  • 1、是否涉及到多线程问题?是,生产者,消费者
  • 2、是否涉及到共享数据?有,产品数量
  • 3、是否涉及到线程的通信?有,存在生产者与消费者的通信
    示例:
 public class TestProduceConsume{
    public static void main(String[] args) {
        Clerk clerk=new Clerk();
        Producer p1=new Producer(clerk);
        Consumer c1=new Consumer(clerk);
        Thread t1=new Thread(p1);//生产者1
        Thread t3=new Thread(p1);//生产者2
        Thread t2=new Thread(c1);//消费者
        t1.setName("生产者1");
        t3.setName("生产者2");
        t2.setName("消费者");
        t1.start();
        t2.start();
        t3.start();

    }
}
class Clerk{
    int product;
    public synchronized void addProduct(){//生产产品
        if(product>=20){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else{
            product++;
            System.out.println(Thread.currentThread().getName()+":生产了第"+product+"个产品");
            notifyAll();
        }

    }
   public synchronized void consumeProduct(){//消费产品

        if(product<=0){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else{
            System.out.println(Thread.currentThread().getName()+"消费了第"+product+"个产品");
            product--;
            notifyAll();
        }
   }

}
class Producer implements Runnable{//生产者
    Clerk clerk;
    public Producer(Clerk clerk){
        this.clerk=clerk;

    }

    public void run(){
        System.out.println("生产者开始生产产品");
        while(true){
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            clerk.addProduct();
        }
    }

}
class Consumer implements Runnable{
    Clerk clerk;
    public Consumer(Clerk clerk){
        this.clerk=clerk;
    }
    public void run(){
        System.out.println("消费者消费产品");
        while(true){
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}



作者: Aquamarine 
链接:https://www.imooc.com/article/22456
来源:慕课网

猜你喜欢

转载自blog.csdn.net/qq_34715484/article/details/79081357