首页 > 编程语言 >Java——多线程(下)

Java——多线程(下)

时间:2024-12-14 17:29:34浏览次数:6  
标签:Java Thread synchronized 线程 new 多线程 public name

一 (线程同步)

1 线程冲突和同步

(有线程冲突——>通过线程同步解决——>并行化转换成线性化)

(线程同步——>一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用)




1.1 线程冲突
在这里插入图片描述

可能动作一刚改完名字,时间片到了,时间片给到动作二,就会产生“张冠李戴”的问题,就是线程冲突

1.2 同步问题的提出

现实生活中,我们会遇到“同一个资源,多个人都想使用”的问题。 比如:教室里,只有一台电脑,多个人都想使用。天然的解决办法就是,在电脑旁边,大家排队。前一人使用完后,后一人再使用。

1.3 线程同步的概念

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。 这时候,我们就需要用到“线程同步”。 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。










2 线程冲突案例演示

(两个取款线程同时对同一个账户取钱)


我们以银行取款经典案例来演示线程冲突现象。

银行取钱的基本流程基本上可以分为如下几个步骤。

(1)用户输入账户、密码,系统判断用户的账户、密码是否匹配。

(2)用户输入取款金额

(3)系统判断账户余额是否大于或等于取款金额

(4)如果余额大于或等于取款金额,则取钱成功;如果余额小于取款金额,则取钱失败。

/**
 * 账户类--------------------------------------------------------------------------------
 */
class Account{
  //账号
  private String accountNo;
  //账户的余额
  private double balance;
    
  public Account() {
   }
  public Account(String accountNo, double balance) {
    this.accountNo = accountNo;
    this.balance = balance;
   }
  public String getAccountNo() {
    return accountNo;
   }
  public void setAccountNo(String accountNo) {
    this.accountNo = accountNo;
   }
  public double getBalance() {
    return balance;
   }
  public void setBalance(double balance) {
    this.balance = balance;
   }
}
/**
 * 取款线程--------------------------------------------------------------------------------
 */
class DrawThread implements Runnable{
  //账户对象
  private Account account;
  //取款金额
  private double drawMoney;
  //构造方法  
  public DrawThread(){}  
  public DrawThread(Account account,double drawMoney){
    this.account = account;
    this.drawMoney = drawMoney;
   }

 /**
  * 取款线程-------------------------------------------------------------------------------
  */
  @Override
  public void run() {
    //判断当前账户余额是否大于或等于取款金额
    if(this.account.getBalance() >= this.drawMoney){
      System.out.println(Thread.currentThread().getName()+" 取钱成功!吐出钞票:"+this.drawMoney);
	  //休眠一秒
      try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
      //更新账户余额
      this.account.setBalance(this.account.getBalance()- this.drawMoney);
      System.out.println("\t 余额为:"+this.account.getBalance());
     }else{
      System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");
     }
   }
}

//主线程----------------------------------------------------------------------------------
public class TestDrawMoneyThread {
  public static void main(String[] args) {
    //初始化账户  
    Account account = new Account("1234",1000);
    //创建两个取款线程——>对同一个账户操作  
    new Thread(new DrawThread(account,800),"老公").start();
    new Thread(new DrawThread(account,800),"老婆").start();
   }
}

在这里插入图片描述

出现线程冲突清空了










3 实现线程同步

(synchronized关键字——>把并行化转化成线性化)

(synchronized(锁对象){ 同步代码 }

——>锁对象里面只能是对象类型

——>拥有相同锁对象的线程才会做线程同步

——>同步代码里面放(原子操作),就是需要把并行转换成串行的)

(synchronized 方法和 synchronized 块——>块可以缩小同步范围,提高效率——>范围越大串行越多,效率慢)





由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。这套机制就是synchronized关键字。

3.1 synchronized语法结构:

  • synchronized(锁对象){   同步代码 }

3.2 synchronized关键字使用时需要考虑的问题:

  • 需要对那部分的代码在执行时具有线程互斥的能力(线程互斥:并行变串行)。
  • 需要对哪些线程中的代码具有互斥能力(通过synchronized锁对象来决定)。

3.3 它包括两种用法:

synchronized 方法和 synchronized 块。

  • synchronized 方法

    通过在方法声明中加入 synchronized关键字来声明,语法如下:

    1
    public synchronized void accessVal(int newVal);
    

    synchronized 在方法声明时使用:放在访问控制符(public)之前或之后。这时同一个对象下synchronized方法在多线程中执行时,该方法是同步的,即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入。

  • synchronized块

    synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

    Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率。

线程联合和线程同步的区别:——>线程同步是“如何安全地共享资源”,而线程联合是“如何正确地结束线程”

线程联合(Thread Joining)
目的:确保一个线程在继续执行之前等待另一个线程完成其任务。
机制:通过join()方法或其他同步辅助类(如CountDownLatch、Future等)来实现线程间的等待关系。
作用范围:通常作用于线程间,用于控制线程的执行顺序,确保某个线程在另一个线程结束后再继续执行。
结果:确保主线程或其他线程在子线程完成工作后再继续执行,避免资源使用上的冲突。

线程同步(Thread Synchronization)
目的:确保多个线程在访问共享资源时的一致性和完整性,防止数据竞争和不一致性问题。
机制:通过锁、信号量、临界区等机制来控制对共享资源的访问,确保同一时间只有一个线程可以执行特定的代码段。
作用范围:通常作用于代码块或方法级别,用于控制对共享资源的并发访问。
结果:防止多个线程同时修改同一数据,确保数据的一致性。

总结:
线程同步关注的是如何在多个线程之间协调对共享资源的访问,以保证数据的一致性和完整性。
线程联合关注的是如何在线程之间建立等待关系,确保一个线程在另一个线程完成后再继续执行。
简而言之,线程同步是关于“如何安全地共享资源”,而线程联合是关于“如何正确地结束线程”。两者在多线程编程中都是非常重要的,但它们解决的问题和使用的场景是不同的。









4 解决线程冲突案例演示

(synchronized加在run方法上面语法上面没有错,但是由于run方法是运行线程比较特殊,加上还是并行)

(确定同步范围——>确定锁对象(同一个账户(this.account))——>只有对同一个账户取钱才同步)



/**
 * 账户类---------------------------------------------------------------------------------
 */
class Account{
  //账号
  private String accountNO;
  //账户余额
  private double balance;
  public Account() {
   }
  public Account(String accountNO, double balance) {
    this.accountNO = accountNO;
    this.balance = balance;
   }
  public String getAccountNO() {
    return accountNO;
   }
  public void setAccountNO(String accountNO) {
    this.accountNO = accountNO;
   }
  public double getBalance() {
    return balance;
   }
  public void setBalance(double balance) {
    this.balance = balance;
   }
}

/**
 * 取款线程-----------------------------------------------------------------------------
 */
class DrawThread implements Runnable{
  //账户对象
  private Account account;
  //取款金额
  private double drawMoney;
  public DrawThread(){}
  public DrawThread(Account account,double drawMoney){
    this.account = account;
    this.drawMoney = drawMoney;
   }

 /**
  * 取款线程体-----------------------------------------------------------------------------
  */
  @Override
  public void run() {
    
    //同步块----------------------------------------------------------------------------- 
    synchronized (this.account){							
      //判断当前账户余额是否大于或等于取款金额
      if(this.account.getBalance() >= this.drawMoney){
        System.out.println(Thread.currentThread().getName()+" 取钱成功!吐出钞票"+this.drawMoney);
        try {
          Thread.sleep(1000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        //更新账户余额
        this.account.setBalance(this.account.getBalance() - this.drawMoney);
        System.out.println("\t 余额为:"+this.account.getBalance());
       }else{
        System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");
       }
     }
   }
}


public class TestDrawMoneyThread {
  public static void main(String[] args) {
    Account account = new Account("1234",1000);
    new Thread(new DrawThread(account,800),"老公").start();
    new Thread(new DrawThread(account,800),"老婆").start();
   }
}

在这里插入图片描述










5 this作为线程对象锁

(在所有线程中——>!!!相同对象!!!——>才会互斥)


语法结构:

synchronized(this){ 
    //同步代码 
   }

public synchronized void accessVal(int newVal){
//同步代码
}
//默让是this——>同一个对象——>才会互斥

示例:

/**
 * 定义程序员类
 */
class Programmer{
  private String name;
  public Programmer(String name){
    this.name = name;
   }
    
  //定义两个方法---------------------------------------------------------------------------
    
  /**
   * 打开电脑
   */
  synchronized public void computer(){
      try {
        System.out.println(this.name + " 接通电源");
        Thread.sleep(500);
        System.out.println(this.name + " 按开机按键");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动中");
        Thread.sleep(500);
        System.out.println(this.name + " 系统启动成功");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
   }
  /**
   * 编码
   */
  synchronized public void coding(){
      try {
        System.out.println(this.name + " 双击Idea");
        Thread.sleep(500);
        System.out.println(this.name + " Idea启动完毕");
        Thread.sleep(500);
        System.out.println(this.name + " 开开心心的写代码");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
}


/**
 * 打开电脑的工作线程
 */
class Working1 extends Thread{
  private Programmer p;
  public Working1(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.computer();
   }
}


/**
 * 编写代码的工作线程
 */
class Working2 extends Thread{
  private Programmer p;
  public Working2(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.coding();
   }
}


//主线程
public class TestSyncThread {
  public static void main(String[] args) {
    Programmer p = new Programmer("张三");
    new Working1(p).start();
    new Working2(p).start();
   }
}









6 字符串作为线程对象锁

(synchronized(“字符串”)——>字符串随便是什么都行,只是个标识(因为字符串是常量,无论如何只要用这个方法,锁肯定一样))

(不同对象——>也会互斥——>所有线程在执行synchronized时都会同步/互斥)



语法结构:

synchronized(“字符串”){ 
    //同步代码 
   }
/**
 * 定义程序员类
 */
class Programmer{
  private String name;
  public Programmer(String name){
    this.name = name;
   }
  
  /**
   * 去卫生间
   */
  public void wc(){
    synchronized ("suibian") {
      try {
        System.out.println(this.name + " 打开卫生间门");
        Thread.sleep(500);
        System.out.println(this.name + " 开始排泄");
        Thread.sleep(500);
        System.out.println(this.name + " 冲水");
        Thread.sleep(500);
        System.out.println(this.name + " 离开卫生间");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}



/**
 * 去卫生间的线程
 */
class WC extends Thread{
  private Programmer p;
  public WC(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.wc();
   }
}

//主线程
public class TestSyncThread {
  public static void main(String[] args) {
    Programmer p = new Programmer("张三");
    Programmer p1 = new Programmer("李四");
    Programmer p2 = new Programmer("王五");
    new WC(p).start();
    new WC(p1).start();
    new WC(p2).start();
   }
}









7 Class作为线程对象锁

(synchronized(XX.class)——>xx就是Class——>是class类在内存中的对象型式——>(Java万物皆对象))

(!!!相同类里面的,不同对象!!!——>中的synchronized会互斥)

(或者synchronized加在静态方法前面)




语法结构:

synchronized(XX.class){ 
    //同步代码 
   }

synchronized public static void accessVal()//在静态方法上添加synchronized

示例:员工领取奖金,相同部门串行化一个一个进去领,不同部门并行化

问:能不能用this——>不行因为是不用的对象

​ 能不能用字符串——>不行,因为是不同员工去不同部门,那样就所以员工去一个领导办公室了

/**
 * 定义销售员工类
 */
class Sale{
  private String name;
  public Sale(String name){
    this.name = name;
   }
  /**
   * 领取奖金
   */
  synchronized public static void money(){			//或者synchornized(Sale.class){}
      try {
        System.out.println(Thread.currentThread().getName() + " 被领导表扬");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 拿钱");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
}
class Programmer{
  private String name;
  public Programmer(String name){
    this.name = name;
   }
  
  /**
   * 领取奖金
   */
  public void money(){
    synchronized (Programmer.class) {
      try {
        System.out.println(this.name + " 被领导表扬");
        Thread.sleep(500);
        System.out.println(this.name + " 拿钱");
        Thread.sleep(500);
        System.out.println(this.name + " 对公司表示感谢");
        Thread.sleep(500);
        System.out.println(this.name + " 开开心心的拿钱走人");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}

/**
 * 程序员领取奖金
 */
class ProgrammerMoney extends Thread{
  private Programmer p;
  public ProgrammerMoney(Programmer p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.money();
   }
}


/**
 * 销售部门领取奖金
 */
class SaleMoney extends Thread{
  private Sale p;
  public SaleMoneyThread(Sale p){
    this.p = p;
   }
  @Override
  public void run() {
    this.p.money();
   }
}


public class TestSyncThread {
  public static void main(String[] args) {
      
    //程序员  
    Programmer p = new Programmer("张三");
    Programmer p1 = new Programmer("李四");
    new ProgrammerMoney(p).start();
    new ProgrammerMoney(p1).start();
    
    //销售部
    Sale s = new Sale("张晓丽");
    Sale s1 = new Sale("王晓红");
    new SaleMoney(s).start();
    new SaleMoney(s1).start();
   }
}
//部门之间并行,部门里的员工串行










8 自定义对象作为线程对象锁

(synchronized,可以使用在线程类的run方法中控制——>或者在普通类调用方法时控制)

(synchronized(自定义对象)——>之前this是在普通类的方法里面用——>自定义对象是在线程类里面用——>效果相同)

(无论是在普通类的方法中使用this作为锁,还是在线程类中使用自定义对象作为锁,只要确保锁对象是相同的,它们的效果是相同的)




语法结构:

synchronized(自定义对象){ 
    //同步代码 
}

示例:经理给每个人敬酒

class Manager{
  private String name;
  public Manager(String name){
    this.name = name;
   }
  public String getName(){
    return this.name;
   }
  /**
   * 敬酒
   */
  public void cheers(String mName,String eName){
      try {
        System.out.println(mName + " 来到 " + eName + " 面前");
        Thread.sleep(500);
        System.out.println(eName + " 拿起酒杯");
        Thread.sleep(500);
        System.out.println(mName + " 和 " + eName + " 干杯");
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
   }
}

/**
 * 敬酒线程类
 */
class CheersThread extends Thread{
  private Manager manager;
  private String name;
  public CheersThread(String name,Manager manager){
    this.name = name;
    this.manager = manager;
   }
  @Override
  public void run() {
    synchronized (this.manager) {					//线程类里面要用自定义对象,不能再用this了
      this.manager.cheers(this.manager.getName(), name);
     }
   }
}


public class TestSyncThread {
  public static void main(String[] args) {
    Manager manager = new Manager("张三丰");
    new CheersThread("张三",manager).start();
    new CheersThread("李四",manager).start();


   }
}









9 什么是线程死锁

(synchronized的使用不当——>多个线程都在等待对方释放对象锁——>引起的线程等待现象)



“死锁”指的是:

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。

某一个同步块需要同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。比如,“化妆线程”需要同时拥有“镜子对象”、“口红对象”才能运行同步块。那么,实际运行时,“小丫的化妆线程”拥有了“镜子对象”,“大丫的化妆线程”拥有了“口红对象”,都在互相等待对方释放资源,才能化妆。这样,两个线程就形成了互相等待,无法继续运行的“死锁状态”。








10 线程死锁案例和解决

(static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();

——>唯一的口红和镜子)

(Synchronized里面又有一个Synchronized,嵌套了,互相等待对方,导致都不动了)

(死锁——> “同步块需要同时持有多个对象锁造成——>要解决这个问题——>同一个代码块,不要同时持有两个对象锁——>不让synchronized嵌套,把它拿出来)




示例:这个程序会一直处在阻塞状态

/**
 * 口红类
 */
class Lipstick{
}


/**
 * 镜子类
 */
class Mirror{
}


/**
 * 化妆线程类
 */
class Makeup extends Thread{
    
    
  private int flag; //flag=0:拿着口红。flag!=0:拿着镜子
  private String girlName;
  static Lipstick lipstick = new Lipstick();
  static Mirror mirror = new Mirror();


  public Makeup(int flag,String girlName){
    this.flag = flag;
    this.girlName = girlName;
   }


  @Override
  public void run() {
    this.doMakeup();
   }
  /**
   * 开始化妆
   */
  public void doMakeup(){
    if(flag == 0){
        
      synchronized (lipstick){				//相当于字符串,因为是static修饰的
        System.out.println(this.girlName+" 拿着口红");
        try {
          Thread.sleep(1000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        //在上一个块里面  
        synchronized (mirror){
          System.out.println(this.girlName+" 拿着镜子");
         }
       }
        
        
     }else{
      synchronized (mirror){
        System.out.println(this.girlName+" 拿着镜子");
        try {
          Thread.sleep(2000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
        synchronized (lipstick){
          System.out.println(this.girlName+" 拿着口红");
         }
       }
     }
   }
}


public class DeadLockThread {
  public static void main(String[] args) {
    new Makeup(0,"大丫").start();
    new Makeup(1,"小丫").start();
   }
}

解决:不让synchronized嵌套,把它拿出来











二 (线程并发协作)

1 生产者/消费者模式

(生产者——>负责生产数据的模块,消费者——>负责处理数据的模块)

(生产者和消费者之间放一个缓冲区——>实现线程的并发协作,解决忙闲不均,提高效率)

(为什么java能做到跨平台性——>虚拟机——>Java代码和操作系统解除耦合度——>的一个中间层)






在这里插入图片描述

多线程环境下,我们经常需要多个线程的并发和协作。这个时候,就需要了解一个重要的多线程并发协作模型“生产者/消费者模式”。

角色介绍

  • 什么是生产者?

    生产者指的是负责生产数据的模块(这里模块可能是:方法、对象、线程、进程)。

  • 什么是消费者?

    消费者指的是负责处理数据的模块(这里模块可能是:方法、对象、线程、进程)。

  • 什么是缓冲区?

    消费者不能直接使用生产者的数据,它们之间有个“缓冲区”。生产者将生产好的数据放入“缓冲区”,消费者从“缓冲区”拿要处理的数据。

缓冲区是实现并发的核心,缓冲区的设置有两个好处:

  1. 实现线程的并发协作

    有了缓冲区以后,生产者线程只需要往缓冲区里面放置数据,而不需要管消费者消费的情况;同样,消费者只需要从缓冲区拿数据处理即可,也不需要管生产者生产的情况。 这样,就从逻辑上实现了“生产者线程”和“消费者线程”的分离,解除了生产者与消费者之间的耦合。

  2. 解决忙闲不均,提高效率

    生产者生产数据慢时,缓冲区仍有数据,不影响消费者消费;消费者处理数据慢时,生产者仍然可以继续往缓冲区里面放置数据 。










2 创建缓冲区

(wait()方法 , notify()方法,notidyAll()方法)

(缓冲区不是线程类,就是一个能存放数据的区域

——>1 定义存放数据的空间

——>2 定义操作空间的索引

——>3 再提供放数据和取数据的方法

——>4 因为放和取操作的是缓冲区同一个空间,要给他们this锁

——>5 放数据之前判断是否已满用while

——>6 如果内存满了调用Object类里面的wait()方法,this.wait(),只能在synchronized里面使用——>执行wait方法时——>线程会将持有的对象锁释放,并进入阻塞状态,其他需要该对象锁的线程就可以继续运行

——>7 取数据时——>判断没有数据就wait等待

——>8 放数据和取数据,都会进入wait状态,用Object类里面的notify()方法,唤醒处于等待状态队列中的一个线程,也只能在synchronized里面使用(notifyAll唤醒所有的)

——>互相唤醒,放在wait方法之外)






/**
 * 定义馒头类------------------------------------------------------------------------------
 */
class ManTou{
  private int id;
  public ManTou(int id){
    this.id = id;
   }
  public int getId(){
    return this.id;
   }
}



/**
 * 定义缓冲区类----------------------------------------------------------------------------
 */
class SyncStack{
  //1 定义存放馒头的盒子
  private ManTou[] mt = new ManTou[10];
  //2 定义操作盒子的索引
  private int index;

    

  /**
   * 3 放馒头-----------------------------------------------------------------------------
   */
  public synchronized void push(ManTou manTou){
    //5 判断盒子是否已满
    while(this.index == this.mt.length){
      try {
        /**6 
         * 语法:wait(),该方法必须要在synchronized块中调用。
         * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
         * 其他需要该对象锁的线程就可以继续运行了。
         */
        this.wait();
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
    //8 唤醒取馒头的线程
    /**
     * 语法:该方法必须要在synchronized块中调用。
     * 该方法会唤醒处于等待状态队列中的一个线程。
     */
    this.notify();
    this.mt[this.index] = manTou;
    this.index++;
   }
    
    
    
  /**
   * 4 取馒头-----------------------------------------------------------------------------
   */
  public synchronized ManTou pop(){
    //7  
    while(this.index == 0){
      try {
        /**
         * 语法:wait(),该方法必须要在synchronized块中调用。
         * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
         * 其他需要该对象锁的线程就可以继续运行了。
         */
        this.wait();
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
    //8 唤醒  
    this.notify();
    this.index--;
    return this.mt[this.index];
   }
}


public class TestProduceThread {
  public static void main(String[] args) {
    
   }
}









3 创建生产者消费者线程

(通过构造方法将缓冲区传递进来)

(生产者和消费者公用同一个缓冲区)



/**
 * 定义生产者线程类
 */
class ShengChan extends Thread{
  private SyncStack ss;
  public ShengChan(SyncStack ss){
    this.ss = ss;
   }
  @Override
  public void run() {
    for(int i=0;i<10;i++){
      System.out.println("生产馒头:"+i);
      ManTou manTou = new ManTou(i);
      this.ss.push(manTou);
    }
   }
}


/**
 * 定义消费者线程类
 */
class XiaoFei extends Thread{
  private SyncStack ss;
  public XiaoFei(SyncStack ss){
    this.ss = ss;
   }
  @Override
  public void run() {
    for(int i=0;i<10;i++){
      ManTou manTou = this.ss.pop();
      System.out.println("消费馒头:"+i);
     }
   }
}



public class ProduceThread {
  public static void main(String[] args) {
      
    //创建缓冲区  
    SyncStack ss = new SyncStack();
    //生产者线程  
    new ShengChan(ss).start();
    //消费者线程  
    new XiaoFei(ss).start();
   }
}









4 线程并发协作总结

(synchronized可阻止并发更新同一个共享资源,实现了同步,但是synchronized不能用来实现不同线程之间的消息传递——>并发协作(也叫线程通信))

(在实际开发中,尤其是“架构设计”中,会大量使用这个模式。 对于初学者了解即可,如果晋升到中高级开发人员,这就是必须掌握的内容)




线程并发协作(也叫线程通信)

生产者消费者模式:

  1. 生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。

  2. 对于生产者,没有生产产品之前,消费者要进入等待状态。而生产了产品之后,又需要马上通知消费者消费。

  3. 对于消费者,在消费之后,要通知生产者已经消费结束,需要继续生产新产品以供消费。

  4. 在生产者消费者问题中,仅有synchronized是不够的。synchronized可阻止并发更新同一个共享资源,实现了同步但是synchronized不能用来实现不同线程之间的消息传递(通信)。

  5. 那线程是通过哪些方法来进行消息传递(通信)的呢?见如下总结:

    方法名作 用
    final void wait()表示线程一直等待,直到得到其它线程通知
    void wait(long timeout)线程等待指定毫秒参数的时间
    final void wait(long timeout,int nanos)线程等待指定毫秒、微秒的时间
    final void notify()唤醒一个处于等待状态的线程
    final void notifyAll()唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先运行
  6. 以上方法均是java.lang.Object类的方法

都只能在同步方法或者同步代码块中使用,否则会抛出异常。

标签:Java,Thread,synchronized,线程,new,多线程,public,name
From: https://blog.csdn.net/2301_80867182/article/details/144473296

相关文章

  • [2253]基于JAVA的玩具贸易智慧管理系统的设计与实现
    毕业设计(论文)开题报告表姓名学院专业班级题目基于JAVA的玩具贸易智慧管理系统的设计与实现指导老师(一)选题的背景和意义在当前全球经济一体化与信息化快速发展的背景下,各行各业的企业管理面临着前所未有的机遇与挑战。玩具贸易行业作为全球消费市场的重要组成部分,其业务......
  • [2339]基于JAVA的电商仓智慧管理系统的设计与实现
    毕业设计(论文)开题报告表姓名学院专业班级题目基于JAVA的电商仓智慧管理系统的设计与实现指导老师(一)选题的背景和意义选题背景与意义:随着电子商务行业的蓬勃发展,电商仓库作为商品流通的重要节点,其管理效率和智能化程度直接影响到整个电商业务链的运营效果和服务质量。......
  • 初学rabbitMQ中的生产者和消费者(JavaSE环境下)
    生产者:publicstaticvoidmain(String[]args)throwsIOException,TimeoutException{//1创建一个连接工厂ConnectionFactoryConnectionFactoryconnectionFactory=newConnectionFactory();//2设置rabbitMQ的ip地址connectio......
  • javaweb足球联赛管理系统 网上商城系统
    目录项目介绍具体实现截图开发核心技术:核心代码部分展示详细视频演示源码获取方式项目介绍随着计算机技术发展,计算机系统的应用已延伸到社会的各个领域,大量基于网络的广泛应用给生活带来了十分的便利。所以把足球联赛管理与现在网络相结合,利用计算机搭建足球联赛管......
  • 11.19[JAVA-WEB]打通JAVA前后端-JSP
    JAVA网页开发(涉及服务器)结构是什么?代码逻辑是怎样的?JAVA网页开发(涉及服务器)结构是什么?代码逻辑是怎样的?(不使用spring框架)HTML、CSS和JavaScript运行在浏览器中,称为前端脚本服务器端脚本是运行在服务器上,动态生成网页(HTML、CSS和JavaScript)的程序。常见服务器端脚本......
  • node.js毕设基于Java的大学生电子产品维修系统的设计与实现 论文+程序
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于大学生电子产品维修系统的研究,现有研究主要以商业电子产品维修服务为主,专门针对大学生群体电子产品维修系统的研究较少。在当今数字化时代,大学生拥......
  • 多线程与线程互斥
    目录引言一、多线程设计多线程模拟抢票二、互斥锁互斥量的接口修改抢票代码锁的原理锁的封装:RAII引言随着信息技术的飞速发展,计算机软件正变得越来越复杂,对性能和响应速度的要求也日益提高。在这样的背景下,多线程编程作为一种提高程序执行效率和资源利用率的技术,......
  • 免费送源码:Java+ssm+MySQL SSM智慧旅游系统 计算机毕业设计原创定制
     摘要随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。智慧旅游系统设计,主要的模块包括查看首页、站点内容(轮播图、公告栏)系统用户(管理员、注册用户、导游)公共内容(旅游资讯、资讯分类)模块管理(地区管理、景点信息......
  • 免费送源码:Java+B/S+MySQL 多元化智能选课系统的设计与实现 计算机毕业设计原创定制
    摘 要多元化智能选课系统使用Java语言的Springboot框架,采用MVVM模式进行开发,数据方面主要采用的是微软的Mysql关系型数据库来作为数据存储媒介,配合前台技术完成系统的开发。论文主要论述了如何使用JAVA语言开发一个多元化智能选课系统,本系统将严格按照软件开发流程进行各个......
  • 5、多线程-按顺序调用,A->B->C,AA 打印 5 次,BB 打印10 次,CC 打印 15 次,重复 10 次
    题目多线程按顺序调用,A->B->C,AA打印5次,BB打印10次,CC打印15次,重复10次代码示例usingSystem;usingSystem.Threading;usingSystem.Threading.Tasks;publicclassABCPrinter{ privateintrepeatCount; privateintaPrintCount; privateintbPrintCount; ......