java之多线程

java之多线程

参考书目: 《java编程思想》 《java典型模块与项目实战大全》

多线程知识点项目实例
多线程同时结束多个学生接完水一起回教室
线程A等待B完成妈妈做菜等儿子打酱油
多线程安全火车站售票系统
线程间通信生产者消费者模型
1. 线程的五个状态

new(创建)
runable(等待运行)、 running(运行)、 blocked(暂停、挂起)
dead(结束)

它们之间的关系如图:

其中由running到runable的中间状态,即blocked(暂停,挂起)
在这里插入图片描述


2. 多线程一:(多个线程同时结束) 学生接完水一起回教室模型

情景描述: Water.java(水龙头对象),Student.java(学生对象),1个水龙头给多个学生接水,要求接完水所有学生一起回教室(即所有线程一起结束);

  • 使用synchronized同步块;
  • 使用wait()使线程挂起,等条件满足时notifyAll()唤醒所有线程;

Water.java

public class Water {
    int number; // 当前已接完水的人数
    public synchronized void giveWater(String name) {
        synchronized (this) {
            ++ number;
            System.out.println(name + "give water");
            try {
                Thread.sleep(3000); // 线程休眠3s,模拟在接水
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "finish water");
            // 下面是精华,如果不满足所有学生都接完水,就将该进程挂起
            if (number < 4) {
                try {
                    wait(); // 使该线程挂起
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                notifyAll(); // 唤醒所有线程(所有人都接完水)
            }
        }
    }
}

Student.java

public class Student extends Thread{
    private String name;
    private Water water;
    public Student(String name, Water water) { // 构造函数
        super();
        this.name = name;
        this.water = water;
    }
    public void receiveWater() {
        System.out.println(name + "run to water-tap");
        water.giveWater(name); // 调用接水的方法
        System.out.println(name + "run back to classroom");
    }
    public void run() { // Thread的run()方法
        receiveWater();
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Water water = new Water(); // 创建唯一的水龙头对象
        Student s1 = new Student("1 ", water);
        Student s2 = new Student("2 ", water);
        Student s3 = new Student("3 ", water);
        Student s4 = new Student("4 ", water);
        s1.start();
        s2.start();
        s3.start();
        s4.start();
    }
}

Output:
在这里插入图片描述
如果不添加 if(number < 4) wait()的限制, 结果如下:
在这里插入图片描述

3. 多线程二:(线程A等待B完成) 妈妈等儿子打酱油模型

情景描述:Son.java(儿子对象),Mother.java(妈妈对象),妈妈做饭儿子去打酱油,妈妈必须要等儿子酱油打回来才可以炒饭;

  • 使用join()方法将son线程加入mother线程,这样只有(打完酱油)son线程结束时,mother线程才会继续运行(接着做饭);

Son.java

public class Son implements Runnable{
    public void run() {
        System.out.println("儿子去买酱油");
        try {
            for (int i=1; i<=5; ++i)  {
                Thread.sleep(1000);
                System.out.println(i + "minute");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("儿子打酱油归来");
    }
}

Mother.java

public class Mother implements Runnable{
    public void run() {
        System.out.println("妈妈准备做饭");
        System.out.println("妈妈发现没有酱油了");
        System.out.println("妈妈让儿子去买酱油");
        Thread son = new Thread(new Son());
        son.start();
        System.out.println("妈妈在等儿子回来");
        try { // 合并妈妈和儿子两个线程
            // 此时妈妈线程必须等儿子线程执行完毕,才可以执行
            son.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("妈妈开始做饭了");
        System.out.println("妈妈做好饭了");
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Thread monther = new Thread(new Mother());
        monther.start();
    }
}

Output:

妈妈准备做饭
妈妈发现没有酱油了
妈妈让儿子去买酱油
妈妈在等儿子回来
儿子去买酱油
1minute
2minute
3minute
4minute
5minute
儿子打酱油归来
妈妈开始做饭了
妈妈做好饭了

如果没有join()的限制,那么妈妈线程会在儿子线程之前执行,即没有酱油就开始做饭:

妈妈准备做饭
妈妈发现没有酱油了
妈妈让儿子去买酱油
妈妈在等儿子回来
妈妈开始做饭了
妈妈做好饭了
儿子去买酱油
1minute
2minute
3minute
4minute
5minute
儿子打酱油归来

4. 多线程之三:(多线程安全) 火车站售票系统

情景描述: Tickets.java(售票口对象),一共4张余票,但有多个售票口,存在同时多人到多个售票口买票的情况,需要保证余票>=0;

  • 通过synchronized()同步方法实现

Tickets.java

public class Tickets implements Runnable {
    int tickets = 4;
    public void run() {
        selltickets();
    }
    public synchronized void selltickets() {
        while (true) {
            if (this.tickets > 0) {
                System.out.println("第"+Thread.currentThread().getName()+"号票口准备卖第"+this.tickets+"张票");
                try {
                    System.out.println("wait for 3 minutes");
                    for (int i=1; i<=3; ++i){
                        Thread.sleep(1000);
                    }
                    System.out.println("第"+Thread.currentThread().getName()+"号票口已卖出第"+this.tickets+"张票");
                    this.tickets --;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Tickets ticket = new Tickets();
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);
        Thread t4 = new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

Output:

第Thread-0号票口准备卖第4张票
wait for 3 minutes
第Thread-0号票口已卖出第4张票
第Thread-0号票口准备卖第3张票
wait for 3 minutes
第Thread-0号票口已卖出第3张票
第Thread-0号票口准备卖第2张票
wait for 3 minutes
第Thread-0号票口已卖出第2张票
第Thread-0号票口准备卖第1张票
wait for 3 minutes
第Thread-0号票口已卖出第1张票
??这里代码存在问题,为什么始终只有第一个线程在运行?


5. 多线程之四:(线程间通信) 生产者消费者模型

情景描述: Producer.java(生产者对象), Consumer.java(消费者对象),StoreHouse.java(数据库对象); 生产者不断添加数据,消费者不断取出数据;如何保证读写之间的一种平衡关系?

  • 问题1: Producer某条数据刚写入一半,就被Consumer取出;
    • 解决方法:使用Synchronized同步块
  • 问题2: 确保StoreHouse中的某条数据,Consumer重复读取多次(写速度跟不上读速度);
    • 解决方法:使用轮询机制

Producer.java

public class Producer implements Runnable {
    StoreHouse s; // 存储库对象
    public Producer(StoreHouse s) { // 构造函数
        this.s = s;
    }
    public void run() {
        int i=0;
        while (true) {
            synchronized (s) { // 创建同步块
                if (s.fullFlag) { // 数据库已经存满
                    try {
                        s.wait(); // 这里目的是让Consumer线程开始运行
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (i == 0) {
                    s.name = "小明";
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    s.sex = "男";
                } else {
                    s.name = "小红";
                    s.sex = "女";
                }
                s.fullFlag = true;
                s.notifyAll(); // 唤醒线程
                i = i+1;
                i %= 2;
            }
        }
    }
}

Consumer.java

public class Consumer implements Runnable {
    StoreHouse s;
    public Consumer(StoreHouse s) {
        this.s = s;
    }
    public void run() {
        while (true) {
            synchronized (s) {
                if (!s.fullFlag) { // 数据库为空
                    try {
                        s.wait(); // 这里是让Producer线程开始运行
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println("-----");
                    System.out.println(s.name + " " + s.sex);
                    s.fullFlag = false;
                    s.notifyAll();
                }
            }
        }
    }
}

StoreHouse.java

public class StoreHouse {
    String name = "no";
    String sex = "no";
    boolean fullFlag = false; // 标记数据库是否存满
}

Main.java

public class Main {
    public static void main(String[] args) {
        StoreHouse s = new StoreHouse(); // 创建一个数据库对象
        new Thread(new Producer(s)).start(); // 启动生产者线程
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Consumer(s)).start(); // 创建消费者线程
    }
}

Output:

-----
小明 男
-----
小红 女
-----
小明 男
-----
小红 女
-----
小明 男
-----
小红 女

略.......
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值