Java线程同步与生产者消费者模型

阅读: 评论:0

Java线程同步与生产者消费者模型

Java线程同步与生产者消费者模型

1、同步代码块解决数据安全问题

  • 安全问题出现的条件

    • 是多线程环境

    • 有共享数据

    • 有多条语句操作共享数据

  • 如何解决多线程安全问题呢?

    • 基本思想:让程序没有安全问题的环境

  • 怎么实现呢?

    • 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可

    • Java提供了同步代码块的方式来解决

  • 同步代码块格式:

    synchronized(任意对象) { 
        多条语句操作共享数据的代码 
    }

        synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁  

  • 同步的好处和弊端

    • 好处:解决了多线程的数据安全问题

    • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

  • 代码演示

    public class SellTicket implements Runnable {private int tickets = 100;private Object obj = new Object();@Overridepublic void run() {while (true) {synchronized (obj) { // 对可能有安全问题的代码加锁,多个线程必须使用同一把锁//t1进来后,就会把这段代码给锁起来if (tickets > 0) {try {Thread.sleep(100);//t1休息100毫秒} catch (InterruptedException e) {e.printStackTrace();}//窗口1正在出售第100张票System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");tickets--; //tickets = 99;}}//t1出来了,这段代码的锁就被释放了}}
    }public class SellTicketDemo {public static void main(String[] args) {SellTicket st = new SellTicket();Thread t1 = new Thread(st, "窗口1");Thread t2 = new Thread(st, "窗口2");Thread t3 = new Thread(st, "窗口3");t1.start();t2.start();t3.start();}
    }

     

2、 同步方法解决数据安全问题

2.1、同步方法的格式

同步方法:就是把synchronized关键字加到方法上

修饰符 synchronized 返回值类型 方法名(方法参数) { 
    方法体;

同步方法的锁对象是什么呢?

        this

2.2、静态同步方法  

同步静态方法:就是把synchronized关键字加到静态方法上

修饰符 static synchronized 返回值类型 方法名(方法参数) { 
    方法体;

同步静态方法的锁对象是什么呢?

        类名.class

 2.3、代码演示

public class MyRunnable implements Runnable {private static int ticketCount = 100;@Overridepublic void run() {while(true){if("窗口一".equals(Thread.currentThread().getName())){//同步方法boolean result = synchronizedMthod();if(result){break;}}if("窗口二".equals(Thread.currentThread().getName())){//同步代码块synchronized (MyRunnable.class){if(ticketCount == 0){break;}else{try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticketCount--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");}}}}}private static synchronized boolean synchronizedMthod() {if(ticketCount == 0){return true;}else{try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}ticketCount--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");return false;}}
}
public class Demo {public static void main(String[] args) {MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);t1.setName("窗口一");t2.setName("窗口二");t1.start();t2.start();}
}

3、Lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

3.1、ReentrantLock构造方法 

方法名 说明 
ReentrantLock()创建一个ReentrantLock的实例

3.2、加锁解锁方法

方法名说明
void lock()获得锁
void unlock()释放锁

3.3、代码演示

 public class Ticket implements Runnable {//票的数量private int ticket = 100;private Object obj = new Object();private ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true) {//synchronized (obj){//多个线程必须使用同一把锁.try {lock.lock();if (ticket <= 0) {//卖完了break;} else {Thread.sleep(100);ticket--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}// }}}}public class Demo {public static void main(String[] args) {Ticket ticket = new Ticket();Thread t1 = new Thread(ticket);Thread t2 = new Thread(ticket);Thread t3 = new Thread(ticket);t1.setName("窗口一");t2.setName("窗口二");t3.setName("窗口三");t1.start();t2.start();t3.start();}}

4、死锁

  • 概述

    线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行

  • 什么情况下会产生死锁

    1. 资源有限

    2. 同步嵌套

  • 代码演示

    public class Demo {public static void main(String[] args) {Object objA = new Object();Object objB = new Object();new Thread(()->{while(true){synchronized (objA){//线程一synchronized (objB){System.out.println("小康同学正在走路");}}}}).start();new Thread(()->{while(true){synchronized (objB){//线程二synchronized (objA){System.out.println("小薇同学正在走路");}}}}).start();}
    }

5、生产者消费者

5.1、生产者和消费者模式概述

概述

生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。

所谓生产者消费者问题,实际上主要是包含了两类线程:

  • 生产者线程用于生产数据
  • 消费者线程用于消费数据

为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库

生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为

消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为

Object类的等待和唤醒方法

方法名说明
void wait()导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
void notify()唤醒正在等待对象监视器的单个线程
void notifyAll()唤醒正在等待对象监视器的所有线程

6、阻塞队列基本使用

6.1、阻塞队列继承结构

6.2、常见BlockingQueue:

ArrayBlockingQueue: 底层是数组,有界

LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值

BlockingQueue的核心方法:
put(anObject)将参数放入队列,如果放不进去会阻塞
take() 取出第一个数据,取不到会阻塞

6.3、代码示例 

public class Demo02 {public static void main(String[] args) throws Exception {// 创建阻塞队列的对象,容量为 1ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1);// 存储元素arrayBlockingQueue.put("汉堡包");// 取元素System.out.println(arrayBlockingQueue.take());System.out.println(arrayBlockingQueue.take()); // 取不到会阻塞System.out.println("程序结束了");}
}

 

 

本文发布于:2024-01-28 16:41:24,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/17064312908806.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:生产者   线程   模型   消费者   Java
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23