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

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

17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(上):https://developer.aliyun.com/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://developer.aliyun.com/article/1580256

目录
相关文章
|
3天前
|
存储 安全 Java
Java-如何保证线程安全?
【10月更文挑战第10天】
|
1月前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
1月前
|
安全 Java 调度
Java 并发编程中的线程安全和性能优化
本文将深入探讨Java并发编程中的关键概念,包括线程安全、同步机制以及性能优化。我们将从基础入手,逐步解析高级技术,并通过实例展示如何在实际开发中应用这些知识。阅读完本文后,读者将对如何在多线程环境中编写高效且安全的Java代码有一个全面的了解。
|
24天前
|
Java 数据中心 微服务
Java高级知识:线程池隔离与信号量隔离的实战应用
在Java并发编程中,线程池隔离与信号量隔离是两种常用的资源隔离技术,它们在提高系统稳定性、防止系统过载方面发挥着重要作用。
20 0
|
29天前
|
存储 缓存 Java
JAVA并发编程系列(11)线程池底层原理架构剖析
本文详细解析了Java线程池的核心参数及其意义,包括核心线程数量(corePoolSize)、最大线程数量(maximumPoolSize)、线程空闲时间(keepAliveTime)、任务存储队列(workQueue)、线程工厂(threadFactory)及拒绝策略(handler)。此外,还介绍了四种常见的线程池:可缓存线程池(newCachedThreadPool)、定时调度线程池(newScheduledThreadPool)、单线程池(newSingleThreadExecutor)及固定长度线程池(newFixedThreadPool)。
|
9天前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
28 1
C++ 多线程之初识多线程
|
25天前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
1月前
|
Java Spring
spring多线程实现+合理设置最大线程数和核心线程数
本文介绍了手动设置线程池时的最大线程数和核心线程数配置方法,建议根据CPU核数及程序类型(CPU密集型或IO密集型)来合理设定。对于IO密集型,核心线程数设为CPU核数的两倍;CPU密集型则设为CPU核数加一。此外,还讨论了`maxPoolSize`、`keepAliveTime`、`allowCoreThreadTimeout`和`queueCapacity`等参数的设置策略,以确保线程池高效稳定运行。
129 10
spring多线程实现+合理设置最大线程数和核心线程数
|
9天前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
31 6
|
7天前
|
存储 运维 NoSQL
Redis为什么最开始被设计成单线程而不是多线程
总之,Redis采用单线程设计是基于对系统特性的深刻洞察和权衡的结果。这种设计不仅保持了Redis的高性能,还确保了其代码的简洁性、可维护性以及部署的便捷性,使之成为众多应用场景下的首选数据存储解决方案。
18 1