java多线程

java的多线程

一、多线程的概述

1.什么是进程,什么是线程?

进程:是一个应用程序(一个软件).

线程:是一个进程中的 执行场景 / 执行单元.

2.线程和线程之间

进程a和进程内存不会独立

线程a和线程b,堆内存和方法区内存共享,但是栈内存独立,一个线程一个栈.

多线程并发:几个线程就有几个栈空间,互不干扰

3.问题

使用多线程机制后,main方法结束,是不是有可能程序也不会结束,main方法结束只是主线程结束了,主栈空了,其他的栈(线程)可能还在运行.

4.多线程并发的理解

对于多核cpu实现多线程并发是可以的.

对于单核cpu真的可以实现多线程并发嘛?

单核无法真的做到多线程并发,但是可以给人一种多线程并发的感觉.先处理一下a线程,再处理b线程,再处理a线程,反复交替,给人以多线程并发的错觉.

二、实现线程的第1种方式

java支持多线程机制,并且java已经将多线程实现了,我们只需要继承就可以了.

第一种方式:编写一个类,直接继承 java.lang.Thread,重写run方法

public class one {
    public static void main(String[] args) {
        thr thr = new thr();//创建一个线程分支对象
        thr.start();
        //start()方法作用在在jvm中开新的栈空间,这段代码的任务就是开辟栈空间,线程就启动成功了
        // 启动成功的线程会自动调用run方法。并且run方法处于分支栈的底部(压栈)
        //main在主栈的栈底部,run和main是平级别的

        //如果直接调用对象的run方法不会分配栈空间,依旧在同一个栈里面(说白了单线程)

        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }

    }
}

class thr extends Thread{
    @Override
    public void run() {
        //编写程序,这段程序就是在分支栈中运行
        for (int i = 0; i < 10 ; i++) {
            System.out.println(i + "分支");
        }
    }
}

三、实现线程的第二种方式

编写一个类,实现java.lang.Runnable接口,实现run方法

public class one {
    public static void main(String[] args) {
        //创建一个可运行的对象
        thr thr = new thr();
        //将可运行的对象封装为一个线程对象,
        // 因为Thread的构造方法可以接收一个实现了Runnable的类
        Thread thread = new Thread(thr);

        thread.start();

        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}

//这并不是一个线程类,是一个可运行的类,还不是一个线程
class thr implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10 ; i++) {
            System.out.println(i + "run");
        }
    }
}

采用匿名内部类的方式

// 里面的 new Runnable()是通过一个没有名字的类new的对象
// 接口无法new对象
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
});

 thread.start();

四、线程的生命周期

在这里插入图片描述

新建:采用 new语句创建完成

就绪:执行 start 后

运行:占用 CPU 时间

阻塞:执行了 wait 语句、执行了 sleep 语句和等待某个对象锁,等待输入的场合

终止:退出 run()方法

五、获取线程对象的名字

获取当前线程对象

获取线程对象的名字

修改线程对象的名字

线程对象名字的操作

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println( "分支线程>>" + i);
        }
    }
});

//获取线程名字
String s = thread.getName();
System.out.println(s); // Thread-0

//修改线程的名字
thread.setName("jack");
System.out.println(thread.getName()); // jack

thread.start();

当线程没有设置名字的时候,默认的名字的规律

Thread-0

Thread-1

Thread-2

六、获取当前线程对象

方法:

static Thread currentThread()
// 返回对当前正在执行的线程对象的引用
// 代码在哪里获取到的就是哪个线程,感觉上像this

// 获取当前线程对象
// 这个方法出现在main方法中,所以当前线程就是主线程
Thread thread = Thread.currentThread();
System.out.println(thread.getName()); // main

Thread ty = new Thread(new Runnable() {
    @Override
    public void run() {
        // 获取当前线程,当前线程表示的就是分支线程
        System.out.println(Thread.currentThread().getName());// Thread-0
    }
});

ty.start();

七、线程sleep方法

static void sleep(long millis)// 参数的毫秒
// 作用:让当前的线程进入休眠,进入“阻塞”状态,放弃占有的cpu时间片
Thread.sleep()

Thread.sleep()方法的面试题

Thread thread = Thread.currentThread();
System.out.println(thread.getName()); // main

Thread ty = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "--->"+i);
        }
    }
});

ty.start();

try {
    ty.sleep(1000 * 2); // 会让线程进入休眠状态吗?
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.println("enmmmm");

不会,因为sleep()方法是一个静态方法和调用的对象没有关系

ty.sleep(1000 * 2); 在执行的时候还是会转化为 Thread.sleep(1000);来执行

让当前的线程也就是main线程休眠

通过异常的方式唤醒sleep

public class one {
    public static void main(String[] args) {
        run run = new run();
        Thread thread = new Thread(run);

        thread.start();

        // 希望2秒过后开始执行
        try {
            Thread.sleep(1000 * 2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 醒来(这种中断睡眠的方式是通过异常处理机制)
        thread.interrupt();

    }
}

class run implements Runnable{

    @Override
    public void run() {
        System.out.println("start");

        // 只能够try-catch,无法上抛是因为,子类不能够比父类抛出更加宽泛的异常
        // 总结:run方法在父类中没有抛出异常,子类只能够try-catch无法上抛
        try {
            Thread.sleep(1000 * 60 * 60 * 24); // 睡眠一天
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("end");
    }
}

强行终止线程

run run = new run();
Thread thread = new Thread(run);

thread.start();

// 希望2秒过后开始执行
try {
    Thread.sleep(1000 * 2);
} catch (InterruptedException e) {
    e.printStackTrace();
}
// 醒来(强行终止thread线程)
thread.stop();//已经过时,不建议使用

缺陷:容易丢失数据,方法会直接将线程终止

合理的终止线程的执行(常用)

public class one {
    public static void main(String[] args) {
        run run = new run();
        Thread thread = new Thread(run);

        thread.start();

        // 希望3秒过后开始执行
        try {
            Thread.sleep(1000 * 3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 醒来
        run.runs = false; // 通过修改判断条件来终止睡眠

    }
}

class run implements Runnable{

    // 打一个标记
    boolean runs = true;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (runs){
                System.out.println(Thread.currentThread().getName() + "-->" + i);
                try {
                    Thread.sleep(1000); // 睡眠一秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                // 终止当前的线程
                return;
            }
        }
    }
}

八、线程的调度(了解)

1.常见的线程调度模型

抢占式调度模型: 那个线程的优先级高,抢到的cpu时间片的概念就高些/多一些

​ java使用的就是抢占式调度模型.

均分式调度模型: 平均分配cpu时间片,每个线程占有的cpu时间片长度一样,平均 分配,一切平等.

2.和调度有关的方法

实例方法:

​ void setPriority(int newPriority) 设置线程的优先级

​ int getPriority() 获取线程的优先级

​ 最低的优先级是:1

​ 默认优先级是:5

​ 最高的是:10

​ void join()

​ 合并线程

run run = new run();
Thread thread = new Thread(run);

try {
    thread.join();
    // 当前的线程进入阻塞,thread线程执行,直到thread线程执行结束,当前线程才可以执行
} catch (InterruptedException e) {
    e.printStackTrace();
}

静态方法:

​ static void yield() 让位方法

​ 暂停当前正在执行的线程对象,让给其他线程,将线程的“运行状态”转变为“就绪状态”

​ 当然让位过后还有可能又抢到了时间片

3.线程的优先级

优先级较高的抢到的cpu时间片相对多一些

System.out.println(Thread.MAX_PRIORITY); // 最高
System.out.println(Thread.NORM_PRIORITY); // 默认
System.out.println(Thread.MIN_PRIORITY); // 最低

Thread thread = Thread.currentThread();
System.out.println("获取当前线程的优先级" + thread.getPriority()); // 5

Thread thread1 = new Thread(new Runnable() {
    @Override
    public void run() {
        //获取线程的优先级
        System.out.println("分支的默认线程优先级" + Thread.currentThread().getPriority());//5
    }
});
thread1.start();

4.线程的让位

当前的线程暂停,回到就绪状态,让给其他线程

静态方法: Thread.yield()

for (int i = 0; i < 100; i++) {
    System.out.println("main" + i);
    if (i % 2 == 0){
        Thread.yield(); // 线程让位
    }
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Thread thread1 = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"--"+ i);
        }
    }
});
thread1.start();

5.线程合并

System.out.println("start");

Thread thread1 = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 6; i++) {
            System.out.println(Thread.currentThread().getName()+"--"+ i);
            try {
                Thread.sleep(1000 * 1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
});
thread1.start();

try {
    thread1.join(); // thread1 合并到当前的线程中,当前线程收阻塞
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.println("end");

九、线程安全的问题

开发中,项目运行在服务器中,而服务器已经将线程创建和启动都实现了。

不需要编写,需要关注的是数据放入到多线程并发的环境下是否安全(重点)

1.什么时候数据在多线程的环境下操作安全问题

​ 三个条件:

​ 多线程并发

​ 有共享数据

​ 共享数据有修改的行为

2.怎么解决线程安全问题

​ 线程排队执行解决线程安全问题,被称为“线程同步机制

​ 线程同步会牺牲效率,以安全为主

十、同步和异步的理解

异步编程模型:

​ 两个线程各自执行自己的,谁也不用等谁,其实就是多线程并发效率高

同步编程模型:

​ 两个线程在执行的时候,t1在执行的时候,必须等待t2线程的执行,两个线程间发生了等待关系,这就是同步编程模型

总结:异步就是并发,同步就是排队

模拟银行取款

此方法模拟的情况是当两个线程对同一个对象进行操作的时候,当两者都调用对象的时候,用户1已经取款但是余额没有刷新,这个时候巧的是用户2也在取款,并且成功取出的情况,两者取出的金额大于余额。

定义银行账户
class Account{
    private String actne; // 账号
    private double balance; // 余额

    public Account(String actne, double balance) {
        this.actne = actne;
        this.balance = balance;
    }

    public Account() {
    }

    // 取款方法
    public void get_money(double d1){
        if (d1 > getBalance()){
            System.out.println("取款大于余额,无法取款");
            return;
        }
        double his = getBalance();
        setBalance(getBalance() - d1);
        System.out.println("取款成功记录");
        System.out.println("--------------------");
        System.out.println("取款前" +his);
        System.out.println("取出" + d1);
        System.out.println("剩余" + getBalance());
    }

    public String getActne() {
        return actne;
    }

    public void setActne(String actne) {
        this.actne = actne;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}
模拟多个线程对银行账号进行操作
// 账号对象
Account account = new Account("jack",1000);

// 两个栈操控堆中的一个对象

Thread1 thread1 = new Thread1(account); // 一个人对账户操作
thread1.setName("t1用户");

Thread1 thread2 = new Thread1(account); // 另外一个人对账户操作
thread2.setName("t2用户");

thread1.start();
thread2.start();

十一、同步代码块synchronized

线程同步机制的语法

// synchronized 后面的小括号传入的数据非常关键,这个数据必须是多线程共享的数据,才能够达到多线程排队
	// () 里面写什么取决于需要哪些线程同步,将需要同步的线程的数据写入
synchronized(){
 	// 线程同步代码块
 }

关于上面银行取款问题的解决方案

public void get_money(double d1){
    // 将以下这几行代码必须排队执行,不能够并发
    // 一个线程执行后,另外一个线程执行
    synchronized(this){
        if (d1 > getBalance()){
        System.out.println("取款大于余额,无法取款");
        return;
    }
    double his = getBalance();
    setBalance(getBalance() - d1);
    System.out.println(Thread.currentThread().getName() + "取款成功记录");
    System.out.println("--------------------");
    System.out.println("取款前" +his);
    System.out.println("取出" + d1);
    System.out.println("剩余" + getBalance());
    }
}

两个线程对同一个账户对象,进行操作所以可以填入this表示当前账户

在java语言中,任何一个对象都有“一把锁”,其实这把锁就是标记(只是把它叫做锁)

100个对象,100把锁,一个对象一把锁

当两个线程执行同一个代码块的时候,肯定存在一个先后关系,当其中一个线程占到先机后遇见synchronized,就给同步代码块上一把锁,这个对象执行不结束锁就无法打开,其余的线程就无法进来.

synchronized的理解(用法,以及可以填入的数据)

在账户里面创建一个实例对象

在账户对象中添加一个Object 类型的对象为实例变量(Account对象是多线程共享的,Account对象中的实例变量也是共享的)

synchronized() 的括号里面只需要放一个共享对象的对象锁就可以,所以也可以将Accoutn账户类里面的一个对象传进来.


填入字符串

synchronized(字符串),因为字符串定义在常量池当中

但是写字符串的话所有的线程都会同步


this

所以this的情况,如果是同一个账户synchronized(this)使用this修饰的话,当两个线程对同一个账户进行操作时候存在排队的情况,但是第三个线程对一个新的账户进行操作的时候可以轻松访问,不会存在影响

this的总结:使用this,不同的线程对同一个账户操作的时候会存在锁的情况

哪些变量有线程安全问题

三大变量

​ 实例变量:堆中

​ 静态变量:方法区

​ 局部变量:栈中

以上三大变量中:局部变量和常量永远也不会存在线程安全问题,因为局部变量不共享。(一个线程一个栈,而局部变量又在栈中)

实例变量在堆中,堆只有一个。

静态变量在方法区中,方法区只有一个

堆和方法区都是多线程共享的,可能存在线程安全问题。

synchronized扩大同步范围

// 账户对象
class Thread1 extends Thread{
    Account account;

    public Thread1(Account account) {
        this.account = account;
    }

    public Thread1() {

    }

    @Override
    public void run() {
        synchronized (account){
            account.get_money(1000);
        }

    }
}

将账户对象的run方法里面的调用取款的方法包裹起来,也可以实现多线程同步

但是扩大了范围,效率更加低了,

而且不能够使用this,因为this表示的是当前的线程对象,当前存在两个线程对象对一个账户进行操作

synchronized出现在实例方法上面

synchronized 出现在实例方法上面的锁一定是this,

缺点

​ 缺乏灵活性

​ synchronized 出现在实例方法上面表示整个方法体都需要同步,会无故扩大同 步的范围,导致程序的效率降低

优点

​ 代码少

// 取款方法
public synchronized void get_money(double d1){
  
        if (d1 > getBalance()) {
            System.out.println("取款大于余额,无法取款");
            return;
        }
        double his = getBalance();
        setBalance(getBalance() - d1);
        System.out.println(Thread.currentThread().getName() + "取款成功记录");
        System.out.println("--------------------");
        System.out.println("取款前" + his);
        System.out.println("取出" + d1);
        System.out.println("剩余" + getBalance());
}

总结:synchronized的三种写法

第一种:同步代码块

​ 比较灵活

​ synchronized(){

​ }

第二种:在实例方法上面使用synchronized

​ 表示共享的对象一定是this

​ 并且同步代码块在整个方法体

第三种:在静态方法上使用synchronized

​ 表示找类锁

​ 类锁永远只有一把

​ 就算创建了无数个对象,也可能只是一把锁

十二、死锁

会让程序停止,不会出现任何异常和错误,也不会结束运行

要求会写死锁,会写才会注意到这个错误

在这里插入图片描述

public class one {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();

        thread1 thread1 = new thread1(o1,o2);
        thread2 thread2 = new thread2(o1,o2);

        thread1.start();
        thread2.start();
    }
}

class thread1 extends Thread{
    Object o1;
    Object o2;
    public thread1(Object o1, Object o2){
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized (o1){

            //睡眠一秒,为确保往下面执行的时候o2已经上锁
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (o2){

            }
        }
    }
}

class thread2 extends Thread{
    Object o1;
    Object o2;
    public thread2(Object o1, Object o2){
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized (o2){
            synchronized (o1){

            }
        }
    }
}

十三、开发中解决线程问题

synchronized 会然程序的执行效率降低,用户体验不好,系统的用户吞吐量降低,在

不得已的情况下选择线程同步机制

第一种方案:尽量使用局部变量代替“实例变量和静态变量”

第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了,(一个线程对应一个对象,100个对象对应100个对象,对象不共享,就,就没有数据安全问题了)

第三种方案:如果不能够使用局部变量,对象也不能够创建多个,这个时候就只能够选择synchronized了,线程同步机制.

十四、守护线程

java里面的线程分类

​ 分为用户线程(以上学的包括主线程)

​ 守护线程(后台线程,比如垃圾回收机制)

守护线程的特点:

​ 一般守护线程是一个死循环

用户线程结束,守护线程自动结束

守护线程用在什么地方?

​ 每天在指定时间系统自动备数据。

​ 这个需要使用定时器,并且我们可以将定时器设置为守护线程。

案例演示:

public class one {
    public static void main(String[] args) {
        Dath dath = new Dath();
        dath.setName("备份数据");

        // 启动线程之前,将线程设置为守护线程
        // 作为守护线程,当主线程执行完毕后,虽然run方法里面操作whi(true)
        //   但是由于设置了守护线程,当需要守护的线程结束运行后,也会结束运行,
        //   如果没有设置守护线程,就会一种执行下去.
        dath.setDaemon(true);
        dath.start();

        // 主线程:主线程用户线程
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Dath extends Thread{
    @Override
    public void run() {
        int i = 0;
        while (true){
            System.out.println(Thread.currentThread().getName() +
                    "-->" + (++i));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

十五、定时器

间隔特定的时间,执行特定的程序。

java实现可以采用多种方式实现:

​ 1)可以使用sleep()设置睡眠时间,没到指定的时间点开始执行任务

​ 2)java的类库中写好了一个java.util.Timer,可以直接拿来用,目前开发中很少用,高级的框架都是支持定时任务的

void  schedule(TimeTask task, Date firstTime, long period)
             // 定时任务类型   定时任务第一次执行时间   延迟多久执行一次
    
    // 安排指定的任务在指定的时间内开始进行重复的固定延迟执
TimeTask 实现了 Runnable接口,是一个线程
TimeTask 是一个抽象类无法创建对象

案例演示:

public class one {
    public static void main(String[] args) {
        // 创建定时器对象
        Timer t1 = new Timer();

        //Timer t2 = new Timer(true); 表示是以守护线程的方式

        //指定定时任务
        Date firsttime = null;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        try {
            firsttime = simpleDateFormat.parse("2021-02-20 20:14:00");
        } catch (ParseException e) {
            e.printStackTrace();
        }

        t1.schedule(new logtim(), firsttime,10000);
    }
}

class logtim extends TimerTask{

    @Override
    public void run() {
        // 编写需要执行的任务
        System.out.println("定时任务执行");
    }
}

十六、实现线程的第三种方式:

FutrueTask方式,实现Callable接口(JDK8新特性)

优点:

这种方式实现的线程可以获取线程的返回值,之前的方式无法获取返回值

缺点:

通过调用get方法获取其他线程的执行结果的时候,当前线程收阻塞,效率较低.

代码演示:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; // JUC包下的,属于java的并发包

public class one {
    public static void main(String[] args) {
        // 第一步:
        // 创建一个实现了Callable接口的类的对象
        myCallable aaaa = new myCallable();

        // 第二步:创建一个未来任务类对象
        FutureTask task = new FutureTask(aaaa); // 参数传入一个Callable接口的实现对象

        // 第三步:创建线程对象
        Thread t = new Thread(task);
        // 启动线程
        t.start();


        // 在main主线程中获取t线程的结果
        try {
            Object ob = task.get();
            // get方法的执行会导致mian主程序受阻,下面的代码会等待get的结果
            // 而get的方法可能需要很久,因为是从另一个线程获取数据
            System.out.println(ob);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


    }
}

// 也可以使用匿名内部类的方式
// Callable 是一个接口
class myCallable implements Callable{

    // call方法相当于run方法,只不过有返回值
    @Override
    public Object call() throws Exception {
        System.out.println("开始执行");
        return "执行完毕";
    }
}

十七、Object类中的wait()和notify()方法(生产者消费者模式)

1.wait()和notify()不是线程对象的方法,是java中任何一个对象都有i的方法,是Object自带的

在这里插入图片描述

2.wati方法的作用?

​ 让调用该方法上面活动的线程进入等待状态,无期限等待,直到被唤醒

3.notify方法作用?

​ 唤醒调用notify()的线程上的的等待的线程

​ notifyAll ()唤醒调用notify的线程上所有线程

4.生产者消费者模式

在这里插入图片描述

仓库是共享的,使用仓库来调用wait和notify方法

生产线程和消费线程达到均衡

wait和notufy建立在线程同步的基上,因为多线程同时操作一个仓库,有线程安全问题

生产一个消费一个

// 仓库类表示存储的数据,使用list集合存储数据,
//  list里面数据个数为0时,表示仓库空了,保证做到这种效果:生成一个消费一个
public class one {
    public static void main(String[] args) {
        // 创建集合表示仓库
        List list = new ArrayList();

        // 生产者
        Thread thread1 = new Thread(new Producer(list));

        // 消费者
        Thread thread2 = new Thread(new Consumer(list));

        thread1.start();
        thread2.start();
    }
}

// 生产线程
class Producer implements Runnable{
    private List lists;

    public Producer(List lists) {
        this.lists = lists;
    }

    public Producer() {
    }

    @Override
    public void run() {
        // 一直生产
        while (true){
            // 给仓库list加锁
            synchronized (lists){
                if (lists.size() > 0){ // 表示仓库存在,不需要生产
                    // 将当前的生产线程睡眠
                    try {
                        lists.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 为仓库添加数据
                Object o1 = new Object();
                lists.add(o1);
                System.out.println(Thread.currentThread().getName() + "-生产->" + o1);
                lists.notifyAll();
            }
        }
    }
}

// 消费者线程
class Consumer implements Runnable{
    private List lists;

    public Consumer(List lists) {
        this.lists = lists;
    }

    public Consumer() {
    }

    @Override
    public void run() {
        // 一直消费
        while (true){
            synchronized (lists){
                if (lists.size() == 0){
                    // 表示仓库里面已经没有了。需要生产线程生产
                    try {
                        lists.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 仓库里面有数据进行消费
                lists.remove(0);
                System.out.println("消费...");
                lists.notifyAll();
            }
        }
    }
}
} catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 为仓库添加数据
            Object o1 = new Object();
            lists.add(o1);
            System.out.println(Thread.currentThread().getName() + "-生产->" + o1);
            lists.notifyAll();
        }
    }
}

}

// 消费者线程
class Consumer implements Runnable{
private List lists;

public Consumer(List lists) {
    this.lists = lists;
}

public Consumer() {
}

@Override
public void run() {
    // 一直消费
    while (true){
        synchronized (lists){
            if (lists.size() == 0){
                // 表示仓库里面已经没有了。需要生产线程生产
                try {
                    lists.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 仓库里面有数据进行消费
            lists.remove(0);
            System.out.println("消费...");
            lists.notifyAll();
        }
    }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值