java线程基础知识整理

目录

线程基本概念

1、java实现线程

2、线程的生命周期

3、线程常用的方法

3.1、sleep()

3.2、interrupt方法

3.3、stop方法

 

4、线程调度

4.1、常见的线程调度模型

扫描二维码关注公众号,回复: 11453674 查看本文章

4.2、java中提供的线程调度方法

4.3、线程让步

4.4、线程合并

4.5、线程安全

4.5.1、线程同步的实现

4.5.2、java中的线程安全性

4.5.3、synchronized总结

4.5.4、死锁

4.6、守护线程

4.6.1、守护线程的特点

4.6.2、一个简单的守护线程的例子

 

5、定时任务

5.1、实现一个定时器

6、通过Callable接口实现一个线程

7、Object类中的wait和notify方法

7.1、wait和notify方法介绍

7.2、生产者和消费者模式

7.3、实现奇偶数的交替输出


 

线程基本概念

1、什么是进程?什么是线程?

进程是一个应用程序,线程是一个进程中的执行场景/执行单元。一个进程可以启动多个线程。在java语言中对于两个线程A和B,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈。 在使用了多线程机制之后,main()方法结束了,只是主线程结束了,主栈空了,但其他线程不一定结束,其他栈(线程)可能还在压栈弹栈。

1、java实现线程

java语言支持多线程机制。并且java已经实现了多线程(java.lang.Thread类和java.lang.Runnable接口)

第一种实现方式(继承java.lang.Thread类并重写run方法)

public class Thread_01 extends Thread {
    @Override
    public void run() {
        super.run();
        System.out.println("第一个线程");
    }

    public static void main(String[] args) {
        Thread thread=new Thread(new Thread_01(),"first_thread");  //创建线程对象
        thread.start();  //启动线程  start方法使线程处于就绪队列,等待CPU调用
    }
}

第二种实现方式(实现java.lang.Runnable接口并实现run方法)

public class Thread_02 implements Runnable {
    @Override
    public void run() {
        System.out.println("第一个线程");
    }
    public static void main(String[] args) {
        Thread thread=new Thread(new Thread_01(),"first_thread");  //创建线程对象
        thread.start();  //启动线程  start方法使线程处于就绪队列,等待CPU调用
    }
}

通常使用第二种方法,因为一个类实现了接口还可以继承其他类

注意:start方法和run方法的区别!!!

run方法不会启动线程。

start方法的作用是:启动一个线程,在JVM中为线程开辟一个新的栈空间。之后start方法就结束了。线程启动成功并进入排队等待序列。等到被CPU调用到,就会自动调用run方法。

2、线程的生命周期

3、线程常用的方法

3.1、sleep()

public static native void sleep(long millis) throws InterruptedException;

作用:让线程进入休眠,进入“阻塞状态”。放弃占有CPU时间片,让其他线程使用。

public class Thread_03 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(new Thread_03_1(),"Thread_03_1");
        thread.start();
        int count=1; //计数器
        while(true){
            System.out.println("Thread_03_1线程沉睡了"+count+++"秒");
            Thread.sleep(1000);
            if(count>5){
                break;
            }
        }
    }

}

class Thread_03_1 implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(1000*5);    //让线程沉睡(等待) 5秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }
}

3.2、interrupt方法

interrupt方法可以中断线程的睡眠,依靠了java异常处理机制

public class Thread_03 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(new Thread_03_1(),"Thread_03_1");
        thread.start();
        int count=1; //计数器
        while(true){
            System.out.println("Thread_03_1线程沉睡了"+count+++"秒");
            Thread.sleep(1000);
            if(count==3){
                System.out.println("打断Thread_03_1睡眠");
                thread.interrupt();
                break;
            }
        }
    }

}

class Thread_03_1 implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(1000*5);    //让线程沉睡(等待) 5秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"醒了");
    }
}

线程Thread_03_1原计划沉睡5秒,在它睡到3秒时,使用interrupt方法打断其睡眠。

3.3、stop方法

stop方法可以强制终止一个线程的执行。不过这种方式容易丢失数据。因为这种方式会直接杀死线程,线程没有保存的数据会丢失。所以不建议使用

public class Thread_04 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Thread_04_1(), "Thread_03_1");
        thread.start();
        //线程Thread_03_1执行5秒后,强制结束线程Thread_03_1
        Thread.sleep(1000*6);
        System.out.println("强制终止线程Thread_03_1");
        thread.stop();    //强制终止线程
    }
}
    class Thread_04_1 implements Runnable{
        @Override
        public void run() {
           for(int i=1;i<1000;i++) {
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread()+"-->"+i+"秒");
           }
        }
    }

建议使用如下方法结束一个线程:在线程类中增加一个布尔类型的变量run,通过改变run的值,来控制线程运行/停止状态。

public class Thread_05 {
    public static void main(String[] args) throws InterruptedException {
        Thread_05_1 t=new Thread_05_1();
        Thread thread = new Thread(t, "Thread_05_1");
        thread.start();
        //等候5秒之后,终止该线程
        Thread.sleep(1000*5);
        t.run=false;
        System.out.println(thread.getName()+"线程已暂停");
        }
    }

    class Thread_05_1 implements Runnable{
        boolean run=true;                      //通过引入一个布尔类型的变量,来标记该线程的状态(运行/停止)
        @Override
        public void run() {
                for (int i = 1; i < 1000; i++) {
                    if (run) {
                        System.out.println(Thread.currentThread().getName() + "-->" + i + "秒");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        /**
                         * return就表示该线程结束了
                         * 如果有什么需要保存的,可以写在return之前
                         */
                        return;
                    }
                }
            }
        }

 

4、线程调度

4.1、常见的线程调度模型

抢占式调度模型、均分式调度模型

4.2、java中提供的线程调度方法

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

int getPriority()       //获取线程优先级

最低优先级:1   默认优先级:5   最高优先级:10

优先级高的线程抢占的CPU时间片就多一些,处于运行状态的时间片就多一些

public class Thread_06 {
    public static void main(String[] args) {
        Thread_06_1 t61=new Thread_06_1();
        Thread_06_2 t62=new Thread_06_2();
        Thread_06_3 t63=new Thread_06_3();
        Thread thread1=new Thread(t61,"Thread_06_1");
        Thread thread2=new Thread(t62,"Thread_06_2");
        Thread thread3=new Thread(t63,"Thread_06_3");
        //设置线程优先级
        thread1.setPriority(1); thread2.setPriority(2); thread3.setPriority(10);
        thread1.start(); thread2.start(); thread3.start();
        System.out.println("主线程优先级为:"+Thread.currentThread().getPriority());
        for(int i=0;i<1000;i++){
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}
    class Thread_06_1 implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
            for(int i=0;i<1000;i++){
                System.out.println(Thread.currentThread().getName()+"-->"+i);
            }
        }
    }

    class Thread_06_2 implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
            for(int i=0;i<1000;i++){
                System.out.println(Thread.currentThread().getName()+"-->"+i);
            }
        }
    }

    class Thread_06_3 implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
            for(int i=0;i<1000;i++){
                System.out.println(Thread.currentThread().getName()+"-->"+i);
            }
        }
    }

4.3、线程让步

yield方法:使当前线程暂停,回到就绪状态,让给其他线程

public static native void yield();
public class Thread_07 {
    public static void main(String[] args) {
        Thread_07_1 tt=new Thread_07_1();
        Thread thread=new Thread(tt,"Thread_07_1");
        thread.start();
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}
    class Thread_07_1 implements Runnable{

        @Override
        public void run() {
            for(int i=0;i<100;i++){
                if(i%10==0){
                    System.out.println(Thread.currentThread().getName()+"暂停了一下");
                    Thread.yield();   //让当前线程暂停一下
                }
                System.out.println(Thread.currentThread().getName()+"-->"+i);
            }
        }
    }

4.4、线程合并

join方法可以使得在t.join()中让CPU优先执行完t。将t合并到当前线程中,使当前线程受阻,t线程执行直到结束。

public class Thread_08 {
    public static void main(String[] args) throws InterruptedException {
        Thread_08_1 thread_08_1=new Thread_08_1();
        Thread_08_2 thread_08_2=new Thread_08_2();
        Thread thread=new Thread(thread_08_1,"Thread_08_1");
        Thread thread1=new Thread(thread_08_2,"Thread_08_2");
        thread.start();
        //合并线程
        thread.join();   //t合并到当前线程中,当前线程受阻塞,t线程执行直到结束
        thread1.start();
        thread1.join();
        //Thread.currentThread().join();
        System.out.println("main over");
    }
}
class Thread_08_1 implements Runnable{

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

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

join()的底层实现代码。

public final void join() throws InterruptedException {
        join(0);
    } 


public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

join()是在底层调用了wait方法,当主线程调用了thread.join()之后,主线程进入此方法,调用join()方法中的wait(0)方法,wait(0)表示无限等待直到被notify。即主线程会无限等待thread线程执行完成。

public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }

4.5、线程安全

数据在多线程并发的环境下会存在安全问题。例如如果多个用户想要修改某个共享的数据,就会引发线程安全问题。因此需要引入线程同步机制(即线程排队执行,不能并发)

4.5.1、线程同步的实现

java里面通过关键字synchronized给线程加锁。线程会获取锁,并独占cpu,只有当线程释放了锁之后,其余线程拿到锁之后才能运行。

当一个线程在运行状态时遇到synchronized关键字,该线程就会放弃占有的cpu时间片,在锁池里面找共享对象的对象锁。

一个线程同步synchronized的例子——模拟ATM机取款

Account类
package ATM;
/*
银行账户,使用线程同步机制,解决线程安全问题
 */
public class Account {
    private String accountName;  //账户名
    private double remain;  //余额
    Object obj=new Object();

    public Account(String accountName, int remain) {
        this.accountName = accountName;
        this.remain = remain;
    }

    public String getAccountName() {
        return accountName;
    }

    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }

    public double getRemain() {
        return remain;
    }

    public void setRemain(double remain) {
        this.remain = remain;
    }
    //取款方法
    public void withdraw(double money) {
        //synchronized (obj){   //obj是一个全局变量,实例一次Account创建一个obj对象,是可以被共享的

        /*Object obj1=new Object();
        synchronized (obj1){*/    //obj1是一个局部变量,每调用一次run方法则会创建一个obj1对象,所以obj1不是共享对象

        //synchronized (null){    //报错:空指针
        //synchronized ("abc"){    //"abc"在字符串常量池当中,会让所有的线程都同步
        synchronized (this){
            double before = this.getRemain();  //取款之前的余额
            double after = before - money;    //取款之后的余额
            try{
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setRemain(after);     //更新余额
        }
    }
}
AccountThread类
package ATM;

public class AccountThread extends Thread {
    //两个线程必须共享同一个账户对象
    private Account act;
    private double money;  //取款金额

    //通过构造方法传递过来构造对象
    public AccountThread(Account act,double money) {
        this.act = act;
        this.money=money;
    }
    @Override
    public void run() {  //run方法执行表示取款操作
       act.withdraw(money);
        System.out.println(Thread.currentThread().getName()+"在账户"+
                act.getAccountName()+"取款"+money+"之后余额为:"+act.getRemain());
    }
}

启动类:Test类

package ATM;

public class Test {
    public static void main(String[] args) {
        Account act=new Account("act001",10000);  //创建账户对象

        //创建两个线程,对同一账户取款
        Thread threadA=new AccountThread(act,5000);
        Thread threadB=new AccountThread(act,3000);

        threadA.setName("小明");
        threadB.setName("小华");

        threadA.start();
        threadB.start();
    }
}

结果:

 //synchronized可以用在实例方法上,此时锁是this
public synchronized void withdraw(double money) {
        double before = this.getRemain();  //取款之前的余额
        double after = before - money;    //取款之后的余额
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.setRemain(after);     //更新余额
    }
}

可以在实例方法上使用synchronized,synchronized出现在实例方法上,锁一定是this,不能是其他的对象了。所以这种方式不灵活。

另外还有一个缺点:synchronized出现在实例方法上,表示整个方法体都需要同步,可能会无故扩大同步的范围,导致程序的执行效率降低。所以这种方式不常用。

4.5.2、java中的线程安全性

java中有三大变量 :1、实例变量(在堆中)  2、静态变量(在方法中) 3、局部变量(在栈中)

局部变量不会有线程安全问题,因为局部变量不共享,局部变量在栈中,一个线程一个栈。

常量不会有线程安全问题,因为常量不会被改变。

实例变量在堆中,堆只有1个。静态变量在方法区中,方法区只有1个。由于堆和方法区都是多线程可共享的,所以实例变量和静态变量可能存在线程安全问题。

ArrayList、HashMap 、HashSet 是非线程安全的

Vector、Hashtable 是线程安全的

4.5.3、synchronized总结

synchronized有三种写法:

第一种:同步代码块(灵活)

synchronized(线程共享对象){

      同步代码块;

}

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

表示共享对象一定是this,并且同步代码块是整个方法体。

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

表示找类锁。

类锁永远只有1把(就算创建了100个对象,那类锁也只有1把)

对象锁:1个对象1把锁,100个对象100把锁。 类锁:100个对象,也可能只是1把类锁。

package Synchronized;

//Q:doOther方法执行的时候需要等待doSome方法的结束吗?
//A:需要,因为静态方法是类锁,不管创建了几个对象,类锁只有1把
public class Exam01 {
    public static void main(String[] args) throws InterruptedException {
        MyClass mc1 = new MyClass();
        MyClass mc2 = new MyClass();
        Thread t1 = new Mythread(mc1);
        Thread t2 = new Mythread(mc2);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        Thread.sleep(1000);
        t2.start();
    }
}

class Mythread extends Thread {
    private MyClass mc;

    public Mythread(MyClass mc) {
        this.mc = mc;
    }

    @Override
    public void run() {
        super.run();
        if (Thread.currentThread().getName().equals("t1")) {
            try {
                mc.doSome();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (Thread.currentThread().getName().equals("t2")) {
            mc.doOther();
        }
    }
}

class MyClass {
    //synchronized出现在静态方法上是走 类锁
    public synchronized static void doSome() throws InterruptedException {
        System.out.println("doSome begin");
        Thread.sleep(1000 * 5);
        System.out.println("doSome over");
    }

    public synchronized static void doOther() {
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}

4.5.4、死锁

一个简单的死锁实现的例子

package Thread;

/*
实现一个死锁,
t1中obj1锁上后就睡了,t2中obj2锁上后就睡了。
t1想要释放obj1锁,就必须请求到obj2锁
t2想要释放obj2锁,就必须请求到obj1锁
t1与t2互相请求锁,但彼此都无法释放锁,所以形成了死锁

 */
public class Dead_lock {
    public static void main(String[] args) {
        Object obj1 = new Object();
        Object obj2 = new Object();

        //t1,t2两个线程共享o1,o2
        Thread t1 = new MyThread_1(obj1, obj2);
        Thread t2 = new MyThread_2(obj1, obj2);
        t1.start();
        t2.start();
    }
}

class MyThread_1 extends Thread {
    Object obj1 = new Object();
    Object obj2 = new Object();

    public MyThread_1(Object obj1, Object obj2) {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        synchronized (obj1) {
            try {
                Thread.sleep(1000 * 3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj2) {

            }
        }
    }
}

class MyThread_2 extends Thread {
    Object obj1 = new Object();
    Object obj2 = new Object();

    public MyThread_2(Object obj1, Object obj2) {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        synchronized (obj2) {
            try {
                Thread.sleep(1000 * 3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (obj1) {

            }
        }
    }
}

注:synchronized在开发中最好不要嵌套使用,一不小心就可能导致死锁现象发生

4.6、守护线程

java语言中线程可分为两大类:

一类是:用户线程       例如:main方法主线程

 一类是:守护线程(后台线程)    例如java垃圾回收线程

4.6.1、守护线程的特点

一般守护线程是一个死循环,所有用户线程只要结束,守护线程自动结束。

守护线程一般会用在一些定时任务,例如每天0点系统自动备份需要用到定时器,我们可以将定时器设置为守护线程一直在那里看着。每到0点就备份一次。所有的用户线程如果结束了,守护线程就自动退出。

4.6.2、一个简单的守护线程的例子

package Thread;

/**
 * 用户线程备份数据,守护线程守护。
 */
public class GuardThread {
    public static void main(String[] args) {
        Thread thread = new BakDataThread();
        thread.setName("备份数据的线程");

        //启动线程之前,将线程设置为守护线程
        thread.setDaemon(true);
        thread.start();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class BakDataThread 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();
            }
        }
    }
}

 

5、定时任务

5.1、实现一个定时器

package Thread;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest {
    public static void main(String[] args) throws ParseException {
        //创建定时器对象
        Timer timer = new Timer();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date firstTime = sdf.parse("2020-07-25 19:10:30");
        //指定定时任务
        //timer.schedule(定时任务,第一次执行时间时间,间隔多久执行一次)
        timer.schedule(new LogTimerTask(), firstTime, 1000 * 5);
    }
}

//编写一个定时任务
class LogTimerTask extends TimerTask {

    @Override
    public void run() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String strTime = sdf.format(new Date());
        System.out.println(strTime + ":成功完成了一次数据备份!");
    }
}

6、通过Callable接口实现一个线程

使用Callable接口实现线程,可以获得该线程的返回值(JDK8新特性)

一个简单的实例:

package Thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThirdWay {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask(new Task());
        Thread thread = new Thread(futureTask);
        thread.start();
        Object object = futureTask.get();  //通过get方法可以获取当前线程的返回值
        ////主线程中这里的程序必须等待get()方法结束才执行
        // get()方法为了拿另一个线程的执行结果需要等待其执行完成,因此要等待较长时间
        System.out.print("线程执行结果:");
        System.out.println(object);
    }
}

class Task implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.println("call method begin");
        Thread.sleep(1000 * 10);
        System.out.println("call method end");
        int a = 100;
        int b = 200;
        return a + b;
    }
}

FutureTask类相关源码
public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

Future类中实现了get方法获取传入线程的返回结果

7、Object类中的wait和notify方法

7.1、wait和notify方法介绍

wait和notify方法不是线程对象的方法,不能通过线程对象调用。

Object object=new Object();
object.wait();//object.wait()让正在object对象上活动的线程进入等待状态,无限等待,直到被唤醒为止
object.notify();//object.notify()唤醒正在object对象上等待的线程
object.notifyAll();//object.notifyAll唤醒正在object对象上等待的所有线程

7.2、生产者和消费者模式

一个简单的生产者和消费者实例

模拟生产者生产一个,消费者就消费一个。让仓库始终零库存。

package Thread;

import java.awt.*;
import java.util.*;
import java.util.List;

public class Producer_Consumer {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        Thread thread1 = new Thread(new Consumer(list), "消费者线程");
        Thread thread2 = new Thread(new Producer(list), "生产者线程");
        thread1.start();
        thread2.start();
    }
}

//消费者线程
class Consumer implements Runnable {
    //仓库
    private List<String> list;

    public Consumer(List<String> list) {
        this.list = list;
    }

    @Override
    public void run() {
        //消费
        while (true) {
            synchronized (list) {
                //如果仓库已经空了,消费者线程等待并释放list集合的锁
                if (list.size() == 0) {
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //仓库中有商品,消费者进行消费
                String str = list.remove(0);
                System.out.println(Thread.currentThread().getName() + " 消费 " + str);
                list.notify();  //唤醒生产者
            }

        }
    }
}

//生产者线程
class Producer implements Runnable {
    //仓库
    private List<String> list;

    public Producer(List<String> list) {
        this.list = list;
    }

    @Override
    public void run() {
        //生产
        while (true) {
            synchronized (list) {
                //如果仓库里有东西,则停止生产。生产者线程等待并释放list集合的锁
                if (list.size() > 0) {  
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                list.add("商品");
                System.out.println(Thread.currentThread().getName() + " 生产 " + list.get(0));
                list.notify();  //唤醒消费者
            }
        }
    }
}

7.3、实现奇偶数的交替输出

package Thread;

/**
 * 使用生产者和消费者模式实现两个线程交替输出:一个线程负责输出奇数,另一个线程负责输出偶数
 */
public class Number {
    public static void main(String[] args) throws InterruptedException {
        Num num = new Num(0);
        Thread thread1 = new Thread(new Odd(num), "Odd");
        Thread thread2 = new Thread(new Event(num), "Event");
        thread1.start();
        thread2.start();
    }
}

class Odd implements Runnable {
    Num num;

    public Odd(Num num) {
        this.num = num;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (num) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (num.getI() % 2 == 0) {
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
                num.notifyAll();
            }
        }
    }
}

class Event implements Runnable {
    Num num;

    public Event(Num num) {
        this.num = num;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (num) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (num.getI() % 2 != 0) {
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
                num.notifyAll();
            }
        }
    }
}

class Num {
    private int i = 0;

    public Num(int i) {
        this.i = i;
    }

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    int printNum() {
        return i++;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42032770/article/details/107536554