JAVA基础 多线程技术学习笔记(V1.0) 3

简介: JAVA基础 多线程技术学习笔记(V1.0)

四、线程的优先级

什么是线程的优先级

每一个线程都是有优先级的,我们可以为每个线程定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程的优先级用数字表示,范围从1到10,一个线程的默认优先级是5。


Java 的线程优先级调度会委托给操作系统去处理,所以与具体的操作系统优先级有关,如非特别需要,一般无需设置线程优先级。


注意


线程的优先级,不是说哪个线程优先执行,如果设置某个线程的优先级高。那就是有可能被执行的概率高。并不是绝对优先执行。

4.1 线程优先级的使用

使用下列方法获得或设置线程对象的优先级。

  • int getPriority();  //获取当前进程的优先级
  • void setPriority(int newPriority); //设置进程的优先级

注意:线程优先级是在线程启动之前就分配的,优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。

package cn.it.bz.Thread;
class Priority implements Runnable{
    private int num = 0;//记录当前线程执行次数
    private boolean flag = true; //生死牌
    @Override
    public void run() {
         while (flag){
             System.out.println("当前线程名称:"+Thread.currentThread().getName()+",执行次数是:"+num++);
         }
    }
    public void stop(){
        flag = false;
    }
}
public class PriorityThread {
    public static void main(String[] args) throws InterruptedException {
        Priority priority1 = new Priority();
        Thread thread1 = new Thread(priority1,"线程1");
        Priority priority2 = new Priority();
        Thread  thread2 = new Thread(priority2,"线程2");
        System.out.println("线程1的优先级:"+thread1.getPriority());//5
        System.out.println("线程2的优先级:"+thread2.getPriority());//5
        thread1.setPriority(Thread.MAX_PRIORITY);// thread1.setPriority(10);
        thread2.setPriority(Thread.MIN_PRIORITY);
          //启动线程
        thread1.start();
        thread2.start();
        //主线程休眠
        Thread.sleep(2000);
        //结束线程
        priority1.stop();
        priority2.stop();
    }
}

五、守护线程

守护线程(即Daemon Thread),是一个服务线程,准确地来说就是服务其他的线程,这是它的作用,而其他的线程只有一种,那就是用户线程。

在Java中有两类线程:

  • User Thread(用户线程):就是应用程序里的自定义线程。
  • Daemon Thread(守护线程):比如垃圾回收线程,就是最典型的守护线程。

守护线程特点:守护线程会随着用户线程死亡而死亡。

守护线程与用户线程的区别:

用户线程,不随着主线程的死亡而死亡。用户线程只有两种情况会死掉,1、在run中异常终

止。2、正常把run执行完毕,线程死亡。

守护线程,随着用户线程的死亡而死亡,当用户线程死亡守护线程也会随之死亡。

5.1 守护线程的使用

package cn.it.bz.Thread;
//守护线程
class Daemon implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("守护线程:"+Thread.currentThread().getName()+","+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//主线程
public class DaemonThread {
    public static void main(String[] args) throws InterruptedException {
        //实例化守护线程
        Thread thread = new Thread(new Daemon(),"啊哈哈");
        thread.setDaemon(true); //将普通线程变为守护线程
        thread.start();  //守护线程启动
        Thread.sleep(3000);
        System.out.println("主线程结束");//主线程结束,守护线程结束。守护线程不一定非得守护主线程。
    }
}

六、线程同步

6.1 什么是线程冲突?

如图,该进程中的线程一在对进程空间中的对象进行修改时,突然时间片用完。此时线程一只是修改了对象的name,age并未做出修改。线程二读取到的对象的值就是错误的。

6.2 同步问题的提出

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

6.3 实现线程同步

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

synchronized语法结构:

synchronized(对象锁){  

  同步代码

  }

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

  • 需要对那部分的代码在执行时具有线程互斥(线程同步)的能力(线程互斥:并行变串行)。
  • 需要对哪些线程中的代码具有互斥能力(通过synchronized锁对象来决定)。
  • 拥有相同对象锁的线程才会做线程互斥。
  • synchronized两种用法:

synchronized 方法


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


public synchronized void accessVal(int newVal);


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


synchronized块


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


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

6.4 线程冲突案例演示

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

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

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

(2)用户输入取款金额

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

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

6.4.1 没有实现线程冲突

package cn.it.bz.Thread;
//账户类
class Account{
    private String password; //账户密码
    private double balance;  //账户余额
    public Account() {
    }
    public Account(String password, double balance) {
        this.password = password;
        this.balance = balance;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
}
//取款线程
class DrawMoneyThread implements Runnable{
    //账户对象
    private Account account;
    //取款金额
    private double drawMoney;
    public DrawMoneyThread() {
    }
    public DrawMoneyThread(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.account.getBalance()-this.drawMoney));
            //线程休眠,账户余额修改
            try {
                Thread.sleep(1000);
                this.account.setBalance(this.account.getBalance()-this.drawMoney);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            System.out.println(Thread.currentThread().getName()+"取款失败!!余额不足");
        }
    }
}
public class TestDrawMoneyThread {
    public static void main(String[] args) {
        Account account = new Account("12345",2000);
        Thread manThread = new Thread(new DrawMoneyThread(account, 1300),"男人取款线程");//男人取款线程
        Thread womanThread = new Thread(new DrawMoneyThread(account, 1000),"女人取款线程");//女人取款线程
        manThread.start();
        womanThread.start();
    }
}

6.4.2 实现线程同步

package cn.it.bz.Thread;
//账户类
class Account{
    private String password; //账户密码
    private double balance;  //账户余额
    public Account() {
    }
    public Account(String password, double balance) {
        this.password = password;
        this.balance = balance;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
}
//取款线程
class DrawMoneyThread implements Runnable{
    //账户对象
    private Account account;
    //取款金额
    private double drawMoney;
    public DrawMoneyThread() {
    }
    public DrawMoneyThread(Account account, double drawMoney) {
        this.account = account;
        this.drawMoney = drawMoney;
    }
    //取款线程体
    @Override
    public void run() {
    //判断当前账户余额>=取款金额
        //同步范围,synchronized不能加在run方法上,因为不起作用。
        synchronized (this.account){  //锁住账户对象,this指的是当前DrawMoneyThread线程对象
            if (this.account.getBalance()>this.drawMoney){
                System.out.println(Thread.currentThread().getName()+"取款成功!"+"余额:"+(this.account.getBalance()-this.drawMoney));
                //线程休眠,账户余额修改
                try {
                    Thread.sleep(1000);
                    this.account.setBalance(this.account.getBalance()-this.drawMoney);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                System.out.println(Thread.currentThread().getName()+"取款失败!!余额不足");
            }
        }
    }
}
public class TestDrawMoneyThread {
    public static void main(String[] args) {
        Account account = new Account("12345",2000);
        Thread manThread = new Thread(new DrawMoneyThread(account, 1300),"男人取款线程");//男人取款线程
        Thread womanThread = new Thread(new DrawMoneyThread(account, 1000),"女人取款线程");//女人取款线程
        manThread.start();
        womanThread.start();
    }
}

6.5 使用this作为线程对象锁

对象锁的作用是决定了哪些线程具有互斥的效果,其类型必须是对象类型不能是基本数据类型。

语法结构:

synchronized(this){

    //同步代码

   }

 

public synchronized void accessVal(int newVal){

//同步代码

}

 

package cn.it.bz.Thread;
//程序员类
class Programmer{
    private String name;
    public Programmer(String name) {
        this.name = name;
    }
//打开电脑
    public void openPC() throws InterruptedException {
        synchronized (this){  //this代表的是张三
            System.out.println(this.name+"接通电源");
            Thread.sleep(1000);//哪个线程执行该方法,哪个线程就会休眠
            System.out.println(this.name+"按开机按键");
            Thread.sleep(1000);
            System.out.println("系统启动中……");
            Thread.sleep(1000);
            System.out.println("系统启动成功!");
        }
    }
//编码
   public void coding() throws InterruptedException {
        synchronized (this){ //this代表的是张三
            System.out.println(this.name+"双击IDEA程序");
            Thread.sleep(1000);
            System.out.println("IDEA程序启动成功");
            Thread.sleep(1000);
            System.out.println("开开心心写代码");
        }
   }
}
//打开电脑的工作线程
class OpenPC implements Runnable{
    private Programmer programmer;
    public OpenPC(Programmer programmer) {
        this.programmer = programmer;
    }
    @Override
    public void run() {
        try {
            this.programmer.openPC();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//编写代码的工作线程
class Coding implements Runnable{
    private Programmer programmer;
    public Coding(Programmer programmer) {
        this.programmer = programmer;
    }
    @Override
    public void run() {
        try {
            this.programmer.coding();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//主线程
public class TestSyncThread {
    public static void main(String[] args) {
        Programmer zs = new Programmer("张三");
        OpenPC openPC = new OpenPC(zs) ;
        Thread threadOpenPC = new Thread(openPC);
        threadOpenPC.start();
        Coding coding = new Coding(zs) ;
        Thread threadCoding= new Thread(coding);
        threadCoding.start();
    }
}

6.6 使用字符串作为线程对象锁

因为字符串是引用类型的数据而且字符串是个常量不会改变,因此所有线程都会同步。

语法结构:

synchronized(“字符串”){

    //同步代码

   }

package cn.it.bz.Thread;
//程序员类
class Programmer{
    private String name;
    public Programmer(String name) {
        this.name = name;
    }
//去卫生间
public void wc() throws InterruptedException {
    synchronized ("随便写"){ //this代表的是张三
        System.out.println(this.name+"打开卫生间门");
        Thread.sleep(1000);
        System.out.println(this.name+"上厕所");
        Thread.sleep(1000);
        System.out.println(this.name+"嘘嘘~");
        Thread.sleep(1000);
        System.out.println(this.name+"离开厕所");
    }
  }
}
//去卫生间的线程
class WC implements Runnable{
    private Programmer programmer;
    public WC(Programmer programmer) {
        this.programmer = programmer;
    }
    @Override
    public void run() {
        try {
            this.programmer.wc();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//主线程
public class TestSyncThread {
    public static void main(String[] args) {
        Programmer zs = new Programmer("张三");
        Programmer ls = new Programmer("李四");
        WC wc1 = new WC(zs) ;
        Thread threadZSWC= new Thread(wc1);
        threadZSWC.start();
        WC wc2 = new WC(ls) ;
        Thread threadLSWC= new Thread(wc2);
        threadLSWC.start();
    }
}

6.7 使用Class作为线程对象锁

语法结构:

synchronized(XX.class){

    //同步代码

   }

 

synchronized public static void accessVal()

package cn.it.bz.Thread;
//销售类
class Sale{
    private String name;
    public Sale(String name) {
        this.name = name;
    }
    //领奖金
    public void money() throws InterruptedException {
        synchronized (Sale.class){
            System.out.println(this.name+"被领导表扬");
            Thread.sleep(1000);
            System.out.println(this.name+"拿钱");
            Thread.sleep(1000);
            System.out.println(this.name+"表示感谢");
            Thread.sleep(1000);
            System.out.println(this.name+"开开心心拿钱走人,O(∩_∩)O");
        }
    }
}
//程序员类
class Programmer{
    private String name;
    public Programmer(String name) {
        this.name = name;
    }
  //领取奖金
    public void money() throws InterruptedException {
        synchronized (Programmer.class){
            System.out.println(this.name+"被领导表扬");
            Thread.sleep(1000);
            System.out.println(this.name+"拿钱");
            Thread.sleep(1000);
            System.out.println(this.name+"表示感谢");
            Thread.sleep(1000);
            System.out.println(this.name+"开开心心拿钱走人,O(∩_∩)O");
        }
    }
}
//程序员领取奖金的线程
class GetMoney implements Runnable{
    private Programmer programmer;
    public GetMoney(Programmer programmer) {
        this.programmer = programmer;
    }
    @Override
    public void run() {
        try {
            this.programmer.money();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//销售领取奖金的线程
class SaleGetMoney implements Runnable{
    private Sale sale;
    public SaleGetMoney(Sale sale) {
        this.sale = sale;
    }
    @Override
    public void run() {
        try {
            this.sale.money();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//主线程
public class TestSyncThread {
    public static void main(String[] args) {
        Programmer zs = new Programmer("张三");
        Programmer ls = new Programmer("李四");
        new Thread(new GetMoney(zs)).start();  //张三领取奖金
        new Thread(new GetMoney(ls)).start();
        Sale sale1 = new Sale("小红");
        Sale sale2 = new Sale("小兰");
        new Thread(new SaleGetMoney(sale1)).start();
        new Thread(new SaleGetMoney(sale2)).start();
    }
}

相同部门的员工在拿钱时是串行的,不同部门之间是并行的。

6.8 使用自定义对象作为线程对象锁

语法结构:

synchronized(自定义对象){

    //同步代码

}

 

package cn.it.bz.Thread;
//经理类
class Manager{
    private String name;
    public Manager(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //敬酒的方法
    public void cheers(String Mname,String Ename) throws InterruptedException {
        synchronized (this){  //manager充当对象锁
            System.out.println(Mname+"经理走到"+Ename+"面前");
            Thread.sleep(1000);
            System.out.println(Ename+"拿起酒杯");
            Thread.sleep(1000);
            System.out.println(Mname+"经理和"+Ename+"干杯");
        }
    }
}
//敬酒的线程
class Cheers implements Runnable{
    private Manager manager; //经理对象
    private String Ename;
    public Cheers(Manager manager, String ename) {
        this.manager = manager;
        Ename = ename;
    }
    @Override
    public void run() {
        try {
/*            synchronized (this.manager){  //或者在线程类中使用经理对象锁
                this.manager.cheers(manager.getName(),Ename);
            }*/
            this.manager.cheers(manager.getName(),Ename);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//主线程
public class TestSyncThread {
    public static void main(String[] args) {
        Manager manager = new Manager("张三");
      Cheers cheers1  = new Cheers(manager,"李四");
      Thread thread1 = new Thread(cheers1);
      thread1.start();
      Cheers cheers2  = new Cheers(manager,"王五");
      Thread thread2 = new Thread(cheers2);
      thread2.start();
    }
}

6.9 线程死锁

6.9.1 死锁的概念

“死锁”指的是:

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


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

package cn.it.bz.Thread;
//口红类
class  Lipstick{
}
//镜子类
class Mirror{
}
//化妆线程
class MakeUp implements Runnable{
    private int flag; //flag = 0表示拿到是口红,否则是镜子
    private String name;  //化妆人的名字
    static Lipstick lipstick = new Lipstick(); //唯一的口红
    static Mirror mirror = new Mirror(); //唯一的镜子
    public MakeUp(int flag,String name){
        this.flag = flag;
        this.name = name;
    }
    //开始化妆
    public void doMakeUp() throws InterruptedException {
        if (this.flag == 0){ //拿到的是口红
            synchronized (lipstick){ //口红只有一份,因此会产生线程互斥
                System.out.println(name+"拿着口红");
                Thread.sleep(1000);
                synchronized (mirror){
                    System.out.println(name+"拿着镜子");
                }
            }
        }else {
            synchronized (mirror){
                System.out.println(name+"拿着镜子");
                Thread.sleep(1000);
                synchronized (lipstick){
                    System.out.println(name+"拿着口红");
                }
            }
        }
    }
    @Override
    public void run() {
        try {
            doMakeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class DeadLockThread {
    public static void main(String[] args) {
        MakeUp makeUp1 = new MakeUp(0,"大丫");
        Thread thread_makeUp1 = new Thread(makeUp1);
        thread_makeUp1.start();
        MakeUp makeUp2 = new MakeUp(1,"小丫");
        Thread thread_makeUp2 = new Thread(makeUp2);
        thread_makeUp2.start();
    }
}

6.9.2 解决线程死锁

死锁是由于 “同步块需要同时持有多个对象锁造成”的,要解决这个问题,思路很简单,就是:同一个代码块,不要同时持有两个对象锁,也就是synchronized中不能嵌套synchronized。

package cn.it.bz.Thread;
//口红类
class  Lipstick{
}
//镜子类
class Mirror{
}
//化妆线程
class MakeUp implements Runnable{
    private int flag; //flag = 0表示拿到是口红,否则是镜子
    private String name;  //化妆人的名字
    static Lipstick lipstick = new Lipstick(); //唯一的口红
    static Mirror mirror = new Mirror(); //唯一的镜子
    public MakeUp(int flag,String name){
        this.flag = flag;
        this.name = name;
    }
    //开始化妆
    public void doMakeUp() throws InterruptedException {
        if (this.flag == 0){ //拿到的是口红
            synchronized (lipstick){ //口红只有一份,因此会产生线程互斥
                System.out.println(name+"拿着口红");
                Thread.sleep(1000);
            }
            synchronized (mirror){
                System.out.println(name+"拿着镜子");
            }
        }else {
            synchronized (mirror){
                System.out.println(name+"拿着镜子");
                Thread.sleep(1000);
            }
            synchronized (lipstick){
                System.out.println(name+"拿着口红");
            }
        }
    }
    @Override
    public void run() {
        try {
            doMakeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class DeadLockThread {
    public static void main(String[] args) {
        MakeUp makeUp1 = new MakeUp(0,"大丫");
        Thread thread_makeUp1 = new Thread(makeUp1);
        thread_makeUp1.start();
        MakeUp makeUp2 = new MakeUp(1,"小丫");
        Thread thread_makeUp2 = new Thread(makeUp2);
        thread_makeUp2.start();
    }
}

七、线程并发协作

7.1 生产者消费者模式介绍

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

角色介绍

  • 什么是生产者?
    生产者指的是负责生产数据的模块(这里模块可能是:方法、对象、线程、进程)。
  • 什么是消费者?
    消费者指的是负责处理数据的模块(这里模块可能是:方法、对象、线程、进程)。
  • 什么是缓冲区?
    消费者不能直接使用生产者的数据,它们之间有个“缓冲区”。生产者将生产好的数据放入“缓冲区”,消费者从“缓冲区”拿要处理的数据。

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

  1. 实现线程的并发协作

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

  1. 解决忙闲不均,提高效率
    生产者生产数据慢时,缓冲区仍有数据,不影响消费者消费;消费者处理数据慢时,生产者仍然可以继续往缓冲区里面放置数据 。

7.2 实现消费者与生产者模式

package cn.it.bz.Thread;
//大饼类
class DaBing{
  private int id;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}
//数据缓冲区,放大饼的,不需要实现线程接口,缓冲区不是线程。
class SyncStack{
    //定义存放大饼的盒子
    private DaBing[] daBings = new DaBing[10];
    //定义操作盒子的索引
    private int index;
    //放大饼
  synchronized  public void setDaBing(DaBing daBing) throws InterruptedException {
      //先判断盒子是否满了
      while (daBings.length == index){
         //满了不生产了让线程等待
         this.wait();  //wait是Object类的方法,该方法必须在synchronized块中调用,wait执行后线程会将持有的对象锁释放并进入阻塞状态。-
      }
      //让消费者来取大饼
      this.notify();
        this.daBings[index] = daBing;
        index++; //注意
    }
    //取大饼
   synchronized public DaBing getDaBing() throws InterruptedException {
      //盒子有饼才能取
       while (index == 0){
           //等待
           this.wait();
       }
       this.notify();//该方法必须在synchronized块中调用,唤醒一个线程
       this.index--;
       return daBings[index];
    }
}
//生产者线程
class ShengChan implements Runnable{
    //生产者生产的大饼需要放到缓冲区
    private SyncStack syncStack;
    public ShengChan(SyncStack syncStack) {
        this.syncStack = syncStack;
    }
    @Override
    public void run() {
       //生产大饼
        for (int i = 0; i < 9; i++) {
            DaBing daBing = new DaBing();
            daBing.setId(i);
            System.out.println("生产大饼"+daBing.getId());
            try {
                syncStack.setDaBing(daBing); //放到缓冲区
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//消费者线程
class XiaoFei implements Runnable{
    //消费者到缓冲区拿
    private SyncStack syncStack;
    public XiaoFei(SyncStack syncStack) {
        this.syncStack = syncStack;
    }
    @Override
    public void run() {
        //取大饼
        for (int i = 0; i < 9; i++) {
            DaBing daBing = new DaBing();
            try {
                DaBing daBing1 = syncStack.getDaBing();//拿大饼
                System.out.println("取大饼"+daBing1.getId());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//主线程
public class TestProduceThread {
    public static void main(String[] args) {
         //创建缓冲区对象
        SyncStack syncStack = new SyncStack();
        //生产者线程
        Thread thread_shengChan = new Thread(new ShengChan(syncStack));
        //消费者线程
        Thread thread_xiaofei = new Thread(new XiaoFei(syncStack));
        thread_shengChan.start();
        thread_xiaofei.start();
    }
}

wait方法就好比张三和李四合租房子。张三去厨房做饭了,然后将厨房的门锁上了,好巧不巧张三此时心脏病犯了,张三马上晕倒了,于是张三把厨房的门打开了,让李四进去了。如果进行对象锁的释放的话就相当于是厨房的门永远锁着,李四永远进不了厨房。

相关文章
|
8天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
2天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
3天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
19 4
|
3天前
|
消息中间件 供应链 Java
掌握Java多线程编程的艺术
【10月更文挑战第29天】 在当今软件开发领域,多线程编程已成为提升应用性能和响应速度的关键手段之一。本文旨在深入探讨Java多线程编程的核心技术、常见问题以及最佳实践,通过实际案例分析,帮助读者理解并掌握如何在Java应用中高效地使用多线程。不同于常规的技术总结,本文将结合作者多年的实践经验,以故事化的方式讲述多线程编程的魅力与挑战,旨在为读者提供一种全新的学习视角。
23 3
|
2天前
|
JSON 前端开发 JavaScript
java-ajax技术详解!!!
本文介绍了Ajax技术及其工作原理,包括其核心XMLHttpRequest对象的属性和方法。Ajax通过异步通信技术,实现在不重新加载整个页面的情况下更新部分网页内容。文章还详细描述了使用原生JavaScript实现Ajax的基本步骤,以及利用jQuery简化Ajax操作的方法。最后,介绍了JSON作为轻量级数据交换格式在Ajax应用中的使用,包括Java中JSON与对象的相互转换。
8 1
|
4天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
17 1
|
7天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
20 3
|
7天前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
7 2
|
7天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
11 1
|
7天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
22 1