0
点赞
收藏
分享

微信扫一扫

012-线程,生产消费模式,线程的通讯

兽怪海北 2021-09-21 阅读 40
JavaJava web

多线程基础

进程和线程
进程
简单来说,每一个应用程序都是一个进程。在创建时需要分配独立的资源以及cpu的执行时间,如果只有一个cpu,不可能做到真正的同时执行。
抢占式多任务:抢到cpu的执行时间,就可以执行。
协作式多任务:如果某个程序正在执行,其他的程序想执行,需要真正执行的程序同意才行。
windows3.x,macOS9以及以前的版本都是协作式,如果程序有问题,可能导致只有这一个程序在执行。后面的系统几乎都使用的抢占式。


线程
一个进程可以有多个线程,至少有一个线程,线程是轻量级的,线程之间共享资源,可以相互协作,能提升性能,但是会带来安全问题。

区别:
关键点就一个,资源独立和资源共享。
多线程的基本实现
使用Thread类
步骤:
1. 继承Thread类
2. 重写run方法
3. 创建子类的对象,调用start方法

基本实现:

public class MyThreadDemo {
    public static void main(String[] args) {
        // 创建子类对象
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        // 调用start方法
        t1.start();
        t2.start();
    }
}

// 在同一个文件中创建了多个类,只能跟文件名同名的类能使用public
/**
 * 实现多线程的步骤
 * @author  
 *
 */
class MyThread extends Thread{
    @Override
    public void run() {
        // 得到当前线程的名称
        String name = Thread.currentThread().getName();
        // 多线程需要执行的任务
        for (int i = 0; i < 100; i++) {
            System.out.println(name + "====" + i);
        }
    }
}

实现售票业务:

/**
 * 多个售票员卖票的场景
 * @author  
 *
 */
public class MyThreadDemo1 {
    public static void main(String[] args) {
        // 创建线程
        Seller zhangsan = new Seller();
        zhangsan.setName("张三");
        Seller lisi = new Seller();
        lisi.setName("李四");
        // 开始卖票
        zhangsan.start();
        lisi.start();
    }
}


class Seller extends Thread{
    static int tickets = 10;
    @Override
    public void run() {
        // 获得线程(售票员)的名称
        String name = Thread.currentThread().getName();
        while(tickets > 0) {
            tickets--;
            System.out.println(name + "卖出一张票...., 剩余票数为:" + tickets);
        }
    }
}
使用Runnable接口

步骤:

1. 实现接口
2. 重写接口中定义的方法
3. 创建实现的对象
4. 使用上面的对象来创建Thread类的对象,并调用start方法

Thread类和Runnable的比较:

1. 使用Thread代码简单,但是占据了继承的位置,无法再继承其他类
2. 使用Runnable接口代码较复杂,但是还可以继承其他类,实现其他接口
3. 得到当前线程名称的方式不一样,继承Thread类也可以使用super.getName(),
但是实现接口只能使用Thread.currentThread().getName()方式。

start方法和run方法区别:

run方法只是重写了线程需要去执行的任务,不包含线程的执行过程。所以当你需要执行线程的时候不能直接调用run方法。而需要调用start方法,start方法表示线程已经进入就绪状态,就等着cpu给执行时间。

基本用法:

/**
 * 使用Runnable接口实现多线程
 * @author  
 *
 */
public class MyRunnableDemo {
    public static void main(String[] args) {
        // 创建runnable的对象
        MyRunnable r = new MyRunnable();
        // 创建线程对象
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);

        // 启动线程
        t1.start();
        t2.start();

        // 主线程
        String name = Thread.currentThread().getName();
        for (int i = 0; i < 100; i++) {
            System.out.println(name + "====" + i);
        }
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        // 得到线程名称
        String name = Thread.currentThread().getName();
        // 业务实现
        for (int i = 0; i < 100; i++) {
            System.out.println(name + "====" + i);
        }
    }

}

实现售票员卖票:

/**
 * 使用runnable实现共享数据的操作(售票员问题)
 * @author  
 *
 */
public class MyRunnableDemo1 {
    public static void main(String[] args) {
        MyRunnable1 r1 = new MyRunnable1();
        // 创建三个售票员
        Thread t1 = new Thread(r1);
        t1.setName("张三");
        Thread t2 = new Thread(r1);
        t2.setName("李四");
        Thread t3 = new Thread(r1);
        t3.setName("王五");
        // 开始卖票
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyRunnable1 implements Runnable{
    static int tickets = 10;
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        while(tickets >0) {
            tickets--;
            try {
                // 休眠
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "卖出一张票,还剩"+tickets+"张票");
        }
    }
}

注意:在main中直接写的代码,执行在主线程中,该线程的名称为main。

多线程的基本方法
线程的名称

见前面代码中的setName,如果不设置名称,系统会自动分配名称,分配的规则是:Thread-0,Thread-1。

线程的休眠

指让当前线程休眠一段时间,以便让其他的线程能够抢占到cpu的执行时间,当休眠时间结束后,当前线程也会处于就绪状态开始抢占执行时间。时间单位:毫秒。

/**
 * 测试线程的休眠
 * @author  
 *
 */
public class MySleepDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            /**
             在main中执行的代码,是在主线程中执行的,当休眠1秒,应该会有其他的线程来抢占cpu的执行时间,
             但此处只有一个主线程,所以呈现出倒计时效果。
             */
            try {
                // 休眠1000毫秒,即1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i+1);
        }
    }
}

线程的优先级

线程可以通过优先级的设置,来使得优先级高的线程能够更多的抢占cpu的执行时间。并不是绝对的。 优先级分为10级。有三个常量:MIN_PRIORITY值为1,MAX_PRIORITY值为10,NORM_PRIORITY值为5(默认值)。

public class MyThreadDemo {
    public static void main(String[] args) {
        // 创建子类对象
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.setPriority(Thread.MAX_PRIORITY); // 设置为最高优先级
        t2.setPriority(Thread.MIN_PRIORITY); // 设置为最低优先级
        // 调用start方法
        t1.start();
        t2.start();
    }
}

注意:优先级高会比优先级低的优先抢占执行时间。但是也不代表优先级低就无法抢占时间,只是比例低而已。

线程的合并

join,将一个线程加入到另一个线程中。

/**
 * 线程的合并
 * @author  
 *
 */
public class MyJoinDemo {
    public static void main(String[] args) {
        // 创建一个线程
        Thread t1 = new MyJoinThread();

        // 使用主线程去打印数据
        String name = Thread.currentThread().getName();
        for (int i = 0; i < 100; i++) {
            // 当执行到20的时候
            if(i == 20) {
                try {
                    // t1线程进入就绪状态
                    t1.start();
                    // 使用合并的方式(,加入,插队),t1执行完后才会接着执行main
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(name + "=====" + i);
        }    
    }
}

class MyJoinThread extends Thread{
    @Override
    public void run() {
        String name = super.getName();
        for (int i = 0; i < 100; i++) {
            System.out.println(name + "=====" + i);
        }
    }
}

线程的生命周期

线程从创建到死亡的过程,中间经历的状态,以及状态转换。

使用new来创建线程,调用start方法,线程进入就绪状态,随即开始抢占cpu的执行时间,抢到时间即进入运行状态,当运行完毕则死亡。 当线程遇到sleep或者join时进入阻塞状态,当sleep的时间结束或者join进来的线程执行完毕,当前线程又回到就绪状态,继续抢占cpu时间。

多线程的使用

线程的通信

线程与线程间建立联系,一个线程能够去与另一个线程进行等待唤醒操作。 使用方法wait(), notify()和notifyAll();

/**
 * wait也能让线程暂停下来(阻塞)
 * 它没有参数,它是Object类自带的对象方法
 * 在暂停线程的同时会释放锁,其他的线程也能够持有锁
 * 线程需要等到唤醒才能继续执行下
 * 线程可以使用任意对象进行wait,也需要通过该对象进行唤醒notify,
 * 如果想一次唤醒该对象wait的所有线程,可以使用notifyAll,建议使用notifyAll,避免有忘记唤醒的线程
 * 注意:
 * 1. 必须要在线程进入wait后,去notify才有效果,先notify是没有意义的
 * 2. 一般情况下,无论是wait还是notify都应该在锁里面执行,会抛出java.lang.IllegalMonitorStateException异常
 * 3. 可以使用任意对象(要注意唯一性)作为锁或者wait,notify的执行者,但是锁的对象和wait或notify的对象要保持一致,否则会抛出java.lang.IllegalMonitorStateException异常
 * @author  
 *
 */
public class MyWaitDemo {
    static Object obj = new Object();
    public static void main(String[] args) {
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                String name = Thread.currentThread().getName(); // 得到当前线程的名称
                for (int i = 0; i < 1000; i++) {
                    System.out.println(name + "====" + i);
                    if(i == 500) {
                        synchronized (this) {
                            try {
                                System.out.println(name + "到达了500------------------------------卡住了");
                                // 当达到500时,休眠10秒
                                obj.wait();
                                System.out.println(name + "20秒休息完毕----------------------------");
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                System.out.println(name + "线程执行完毕==============");
            }
        };
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r1);
        t1.start();
        t2.start();
        System.out.println("5秒后我来救你们=========");
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
                System.out.println(i + "=========");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        synchronized (obj) {
            obj.notifyAll();
//            r1.notify();
        }
    }
}

生产消费模式

设计模式是解决软件某一类业务的方案,该方案经过许多项目的验证,得出经验的总结叫设计模式。 23种设计模式是90年代4人组提出并编写的内容,应对是当时软件行业的业务问题的分类。到现在,设计模式远远不止23种,例如,mvc模式就不是23种之一。

所谓的生产消费模式就是利用多线程的wait和notify的特征实现的,会创建生产者和消费者两类对象,生产者只负责生产,消费者只负责消费,以达到解耦的目的。

/**
 * 生产消费模式案例
 * @author  
 *
 */
public class MyProduceConsumeDemo {
    public static void main(String[] args) {
        Storage s = new Storage();
        for (int i = 0; i < 3; i++) {
            Producer p = new Producer(s);
            p.setName("厂房" + i);
            p.start();
        }
        for (int i = 0; i < 10; i++) {
            Consumer c = new Consumer(s);
            c.setName("4S店,编码:" + i);
            c.start();
        }
    }
}
/**
 * 生产厂房在生产汽车
 */
class Producer extends Thread{
    private Storage s; // 持有仓库对象
    public Producer(Storage s) { // 构造方法
        this.s = s;
    }
    @Override
    public void run() {
        String name = super.getName(); // 当前厂房的名称
        while(true) {
            try {
                Thread.sleep(300); // 每次生产需要3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "生产了一辆汽车");
            synchronized (s) { // 操作共享数据,进行锁定
                s.count++; // 数量+1
                System.out.println("仓库里共有" + s.count + "辆汽车");
                if(s.count > 9) { // 当仓库的数量大于一定的量,通知所有的4s店来拉车
                    System.out.println("快爆仓了,赶紧来拉货====================");
                    s.notifyAll();
                }
            }
        }
    }
}
/**
 * 4s在拉去汽车
 */
class Consumer extends Thread{
    private Storage s;
    public Consumer(Storage s) {
        this.s = s;
    }
    @Override
    public void run() {
        String name = super.getName(); // 当前4s店的名称
        while(true) {
            try {
                Thread.sleep(500); // 每次拉走汽车需要5秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "拉走了一辆汽车");
            synchronized (s) { // 操作共享数据,进行锁定
                if(s.count > 0) { // 如果仓库还有车,就拉走
                    s.count--; // 数量-1
                    System.out.println("仓库里共有" + s.count + "辆汽车");
                }else {
                    System.out.println(name + "白跑一趟...........");
                    try {
                        // 等待汽车的生产
                        System.out.println(name + "开始等待--------");
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

// 仓库
class Storage{
    public Integer count = 0; // 汽车数量
}

举报

相关推荐

0 条评论