博客园 首页 私信博主 显示目录 隐藏目录 管理

Java线程

Java线程

线程,进程
  • 进程(Process):在操作系统中运行的程序就是进程

    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念

    而进程是程序执行过程,它是一个动态的概念,是系统资源分配的单位

    通常一个进程包含若干个线程,进程至少拥有一个线程,否则没有存在的意义。线程是CPU调度和执行的单位

  • 进程(Thread):

    线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,线程没有自己的虚拟地址空间,与进程内的其他线程一起共享分配给该进程的所有资源。

    线程就是独立的执行路径

    在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程

    main()称之为主线程,为系统的入口,用于执行整个程序

    在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器时与操作系统密切相关的,先后顺序时不能人为的干预的

    对同一份资源操作时,会存在资源抢夺问题,需要加入并发控制

    线程会带来额外的开销,如CPU调度时间,并发控制开销

    每个线程在自己的工作内存交互,内存控制不当会导致数据不一0致

线程实现

Thread、Runnable、Callable

1、继承Thread类

继承Thread,重写run方法

调用start方法

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程");
        }
    }
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程");
        }
    }
}

结果:

主线程
主线程
主线程
主线程
主线程
主线程
主线程
主线程
主线程
子线程
主线程
子线程
子线程
子线程
子线程
子线程
子线程
子线程
子线程
子线程

线程开启不是立即执行,而是有CPU调度安排

调用run方法

public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程");
        }
    }

    public static void main(String[] args) {

        MyThread thread = new MyThread();

        thread.run();

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

结果

子线程
子线程
子线程
子线程
子线程
子线程
子线程
子线程
子线程
子线程
主线程
主线程
主线程
主线程
主线程
主线程
主线程
主线程
主线程
主线程

当调用start方法时,主线程和子线程时同时进行的,当输出的循环数据量大的情况下,会出现主线程和子线程交替输出

调用run方法时,是按照从上到下顺序执行的

2、实现Runnable接口

实现runnable接口,重写run方法

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程");
        }
    }
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程");
        }
    }   
}

结果

主线程
主线程
主线程
子线程
子线程
子线程
子线程
子线程
子线程
子线程
子线程
子线程
主线程
主线程
主线程
主线程
主线程
主线程
主线程
子线程
3、实现Callable接口

实现Callable接口,重写call方法,需要返回值

public class TestCallable implements Callable {
    @Override
    public Boolean call() throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "子线程");
        }
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable c1 = new TestCallable();
        TestCallable c2 = new TestCallable();
        TestCallable c3 = new TestCallable();
        //创建执行服务
        ExecutorService pool = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> s1 = pool.submit(c1);
        Future<Boolean> s2 = pool.submit(c2);
        Future<Boolean> s3 = pool.submit(c3);
        //获取结果
        s1.get();
        s2.get();
        s3.get();
        //关闭服务
        pool.shutdown();
    }
}

结果

pool-1-thread-2子线程
pool-1-thread-2子线程
pool-1-thread-2子线程
pool-1-thread-2子线程
pool-1-thread-2子线程
pool-1-thread-3子线程
pool-1-thread-3子线程
pool-1-thread-3子线程
pool-1-thread-1子线程
pool-1-thread-1子线程
pool-1-thread-1子线程
pool-1-thread-1子线程
pool-1-thread-1子线程
pool-1-thread-3子线程
pool-1-thread-3子线程
静态代理
/**
 * 静态代理
 */
public class StaticProxy {

    public static void main(String[] args) {
        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
    

}
interface Marry{
    void HappyMarry();
}
//真实的结婚对象
class You implements Marry{

    @Override
    public void HappyMarry() {
        System.out.println("真实对象结婚了。。。");
    }
}
//代理的结婚对象
class WeddingCompany implements Marry{

    private Marry target;

    public WeddingCompany(Marry target){
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();//这个是真实的对象在执行结婚方法
        after();
    }

    private void before(){
        System.out.println("结婚之前,布置现场");
    }
    private void after(){
        System.out.println("结婚之后,收取尾款");
    }
}

真实对象和代理对象都要实现同一个接口

代理对象中需要通过构造函数传入真实对象

好处:代理对象可以对真实对象进行功能增强,真实对象可以专注于自身方法的实现

线程状态

1、创建状态

2、就绪状态

3、阻塞状态

4、运行状态

5、死亡状态

线程方法
方法 说明
setPriority(int newPriority) 更改线程的优先级
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
void join() 等待该线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void interrupt() 中断线程(建议不要使用这种方式)
boolean isAlive() 测试线程是否处于活动状态
线程停止

stop和destroy方法都已经被废弃,建议不使用

建议线程正常停止,利用次数(不建议死循环),或者使用标志位

示例:

public class TestThread implements Runnable{

    private volatile boolean temp = true;

    @Override
    public void run() {
        while (temp){
            System.out.println("执行中。。。");
        }
    }

    public void stopping(){
        this.temp = false;
        System.out.println("调用线程终止");
    }


    public static void main(String[] args) throws InterruptedException {
        TestThread thread = new TestThread();
        new Thread(thread).start();
        Thread.sleep(2);
        for (int i = 0; i < 1000; i++) {
            if(i == 99){
                thread.stopping();
            }
        }
    }
}

结果:

执行中。。。
执行中。。。
执行中。。。
执行中。。。
执行中。。。
调用线程终止
线程休眠

sleep()

每一个对象都存在锁,sleep不会释放锁

//模拟网络延时:放大问题的发生性
try {
    Thread.sleep(100);
} catch (InterruptedException e) {
    e.printStackTrace();
}
//模拟倒计时
public static void main(String[] args) {
    try {
        tenDown();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
public static void tenDown() throws InterruptedException {
    int num = 10;
    while (num >= 0){
        Thread.sleep(1000);
        System.out.println(num--);
    }
}
线程礼让

yield()

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程由运行状态变成就绪状态
  • 让CPU重新调度,礼让不一定成功,看CPU调度结果
线程强制执行

join()

join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

public class TestJoin implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 100; i++) {
            System.out.println("Vip来了");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread join = new Thread(testJoin, "Join");
        join.start();

        for (int i = 0; i < 300; i++) {
            if(i == 200){
                join.join();//主线程主动自己阻塞,让testjoin线程执行
            }
            System.out.println("main" + i);
        }
    }
}
线程状态观察

getState()

  • NEW 尚未启动的线程
  • RUNNABLE 再Java虚拟机中执行的线程
  • BLOCKED 被阻塞等待监视器锁定的线程
  • WAITING 正在等待另一个线程特定动作的线程
  • TIMED WAITING 正在等待另一个线程达到指定等待时间的线程
  • TERMINATED 已退出的线程
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("///////");
        });
        //观察状态
        Thread.State state = thread.getState();
        System.out.println("创建" + state);

        thread.start();
        state = thread.getState();
        System.out.println("start" + state);

        while(state != Thread.State.TERMINATED){//只要线程状态不中止,就一直输出状态
            Thread.sleep(100);
            state = thread.getState();
            System.out.println("state" + state);
        }
    }
}

结果:

创建NEW
startRUNNABLE
stateTIMED_WAITING
stateTIMED_WAITING
stateTIMED_WAITING
...
stateTIMED_WAITING
///////
stateTERMINATED
线程优先级

Java提供了一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度那个线程来执行

线程优先级通过1-10数据来表示

Thread.MIN_PRIORITY = 1

Thread.MAX_PRIORITY = 10

Thread.NORM_PRIORITY = 5

通过getPriority()获取优先级,通过 setPriority()来设置优先级

线程的优先级高不一定会先执行,只能说优先级高,先执行的概率大

public class TestPriority{
    public static void main(String[] args) {
        MyPriority myPriority = new MyPriority();
        Thread aa = new Thread(myPriority, "AA");
        Thread ab = new Thread(myPriority, "AB");
        Thread ac = new Thread(myPriority, "AC");
        Thread ad = new Thread(myPriority, "AD");
        Thread ae = new Thread(myPriority, "AE");
        Thread af = new Thread(myPriority, "AF");

        af.setPriority(7);
        ac.setPriority(6);
        ad.setPriority(1);

        aa.start();
        ab.start();
        ac.start();
        ad.start();
        ae.start();
        af.start();
    }
}

class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---->" + Thread.currentThread().getPriority());
    }
}

结果:

AF---->7
AC---->6
AA---->5
AB---->5
AE---->5
AD---->1
守护线程
  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 守护线程例如后台记录操作日志,监控内存,垃圾回收等
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认false表示是用户线程,设置为true表示线程为守护线程
        thread.start();//守护线程启动

        new Thread(you).start();//用户线程启动
    }
}
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("上帝一直保佑你");
        }
    }
}
class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你舒服的活着");
        }
        System.out.println("你离开了人世");
    }
}
线程同步机制

多线程处理问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这个时候就需要线程同步,线程同步是一种等待机制,多个需要同时访问此对象的线程进入这个对象等待池形成队列,等待前面的线程使用完毕,下一个线程再使用

线程同步主要由队列和锁构成

由于同一进程的多个线程共享一块存储空间,在带来方便的同时也存在访问冲突问题,为了保证访问的数据正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排他锁,独占资源,其他线程都必须等待,使用后释放锁即可,存在以下问题:

  • 一个线程持有锁会导致其他所有需要此锁的线程挂起
  • 再多线程竞争下,加锁和释放锁会导致比较多的上下文切换和调度延时,引起性能问题
  • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

案例1:抢票

//抢票
public class UnSafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"张三").start();
        new Thread(buyTicket,"黄牛").start();
        new Thread(buyTicket,"李四").start();
    }
}
class BuyTicket implements Runnable{
    private int ticketNum = 10;
    private boolean flag = true;
    @Override
    public void run() {
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //买票
    public void buy() throws InterruptedException {
        if(ticketNum <=0){
            flag = false;
            return;
        }
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + "拿到第" + ticketNum-- + "张票");
    }
}

结果:

张三拿到第10张票
黄牛拿到第10张票
李四拿到第9张票
张三拿到第8张票
李四拿到第7张票
黄牛拿到第8张票
李四拿到第6张票
张三拿到第5张票
黄牛拿到第4张票
李四拿到第3张票
张三拿到第2张票
黄牛拿到第1张票
李四拿到第0张票
张三拿到第-1张票

案例2:银行取钱

public class UnSafeBank {
    public static void main(String[] args) {

        Account account = new Account(1000000, "结婚基金");

        Drawing drawing = new Drawing(account, 90000, "张三");
        Drawing girl = new Drawing(account, 1000000, "girl");
        drawing.start();
        girl.start();
    }
}

class Account{
    int money;//余额
    String name;//卡号

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

class Drawing extends Thread{
    Account account;//账户
    int drawingMoney;//取钱金额
    int nowMoney;//手里余额

    public Drawing(Account account, int drawingMoney, String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {
        if(account.money-drawingMoney < 0){
            System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money = account.money - drawingMoney;
        nowMoney = nowMoney + drawingMoney;
        System.out.println(account.name + ":余额为" + account.money);
        System.out.println(this.getName() + "手里的钱" + nowMoney);
    }
}

结果:

结婚基金:余额为910000
结婚基金:余额为-90000
张三手里的钱90000
girl手里的钱1000000
案例三:线程不安全的集合

ArrayList

public class UnSafeList {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

结果:

9994

同步方法

synchronized 方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized 方法都必须获得调用该方法的对象的锁才能执行,否则会线程阻塞,方法一旦执行,就独占该锁,直到方法返回才释放锁,其他被阻塞的线程才能获得这个锁,继续执行

缺陷:如果一个大的方法被synchronized 修饰,将大大影响性能效率

synchronized方法默认锁的是当前this对象

synchronized块可以选定锁的对象

抢票修改

public class UnSafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"张三").start();
        new Thread(buyTicket,"黄牛").start();
        new Thread(buyTicket,"李四").start();
    }
}
class BuyTicket implements Runnable{
    private int ticketNum = 10;
    private boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //买票
    //synchronized 锁住的对象是默认当前对象 BuyTicket
    public synchronized void buy() throws InterruptedException {
        if(ticketNum <=0){
            flag = false;
            return;
        }
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + "拿到第" + ticketNum-- + "张票");
    }
}

结果:

黄牛拿到第10张票
黄牛拿到第9张票
黄牛拿到第8张票
黄牛拿到第7张票
黄牛拿到第6张票
张三拿到第5张票
张三拿到第4张票
张三拿到第3张票
张三拿到第2张票
张三拿到第1张票

银行取钱修改

public class UnSafeBank {
    public static void main(String[] args) {

        Account account = new Account(1000000, "结婚基金");

        Drawing drawing = new Drawing(account, 90000, "张三");
        Drawing girl = new Drawing(account, 1000000, "girl");
        drawing.start();
        girl.start();
    }
}

class Account{
    int money;//余额
    String name;//卡号

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

class Drawing extends Thread{
    Account account;//账户
    int drawingMoney;//取钱金额
    int nowMoney;//手里余额

    public Drawing(Account account, int drawingMoney, String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {
        //synchronized锁定的是共享资源对象
        synchronized (account) {
            if (account.money - drawingMoney < 0) {
                System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money = account.money - drawingMoney;
            nowMoney = nowMoney + drawingMoney;
            System.out.println(account.name + ":余额为" + account.money);
            System.out.println(this.getName() + "手里的钱" + nowMoney);
        }
    }
}

结果:

结婚基金:余额为910000
张三手里的钱90000
girl钱不够,取不了

集合修改

public class UnSafeList {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                synchronized (list) {
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

结果

10000

JUC安全集合

//测试JUC安全类型的集合
public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

结果

10000
死锁

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

//死锁:多个线程互相只有对方需要的资源,然后形成僵持
public class DeadLock {
    public static void main(String[] args) {
        Makeup m1 = new Makeup(0, "小美", "小美");
        Makeup m2 = new Makeup(1, "小红", "小红");
        m1.start();
        m2.start();
    }

}

//口红
class Lipstick{

}

//镜子
class Mirror{

}

class Makeup extends Thread{
    //static保证资源只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String girlName;//女生名字


    Makeup(int choice,String girlName,String name){
        super(name);
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        if(choice == 0){
            synchronized (lipstick){
                System.out.println(Thread.currentThread().getName() + "获取了口红");
                Thread.sleep(1000);
                synchronized (mirror){
                    System.out.println(Thread.currentThread().getName() + "获取了镜子");
                }
            }
        }else {
            synchronized (mirror){
                System.out.println(Thread.currentThread().getName() + "获取了镜子");
                Thread.sleep(1000);
                synchronized (lipstick){
                    System.out.println(Thread.currentThread().getName() + "获取了口红");
                }
            }
        }
    }
}

结果

小红获取了镜子
小美获取了口红

两个女生都拿到了第一份资源,但是获取不到第二份资源,线程无法结束,导致资源无法释放,程序卡死

解决方法,将synchronized块中的synchronized放到外面

private void makeup() throws InterruptedException {
        if(choice == 0){
            synchronized (lipstick){
                System.out.println(Thread.currentThread().getName() + "获取了口红");
                Thread.sleep(1000);
            }
            synchronized (mirror){
                System.out.println(Thread.currentThread().getName() + "获取了镜子");
            }
        }else {
            synchronized (mirror){
                System.out.println(Thread.currentThread().getName() + "获取了镜子");
                Thread.sleep(1000);
            }
            synchronized (lipstick){
                System.out.println(Thread.currentThread().getName() + "获取了口红");
            }
        }
    }

产生死锁的四个条件:

  • 互斥条件:一个资源每次只能一个进程使用
  • 请求与保持条件:一个进程因请求资源导致阻塞时,对于已经持有的资源不释放
  • 不剥夺条件:进程已获得的资源,在未使用完之前,不得剥离
  • 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系
Lock锁

(1)Lock和ReadWriteLock是两大锁的根接口,Lock代表实现类是ReentrantLock(可重入锁),ReadWriteLock(读写锁)的代表实现类是ReentrantReadWriteLock。

Lock 接口支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则。主要的实现是 ReentrantLock。
ReadWriteLock 接口以类似方式定义了一些读取者可以共享而写入者独占的锁。此包只提供了一个实现,即 ReentrantReadWriteLock,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、适用于非标准要求的实现。
(2)Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。

//测试Lock锁
public class TestLock {
    public static void main(String[] args) {
        TestLock4 testLock4 = new TestLock4();
        new Thread(testLock4).start();
        new Thread(testLock4).start();
        new Thread(testLock4).start();
    }
}

class TestLock4 implements Runnable{
    int ticketNum = 10;

    //定义Lock锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {
                lock.lock();
                if (ticketNum <= 0) {
                    break;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNum--);
                }
            }finally {
                lock.unlock();
            }
        }
    }
}

结果:

10
9
8
7
6
5
4
3
2
1
线程通信

线程间通信方法

方法名 作用
wait() 表示线程一直等待,一直到其他线程通知,与sleep不同,会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

生产者消费者模型

生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。生产者生成一定量的数据放到缓冲区中,然后重复此过程;与此同时,消费者也在缓冲区消耗这些数据。生产者和消费者之间必须保持同步,要保证生产者不会在缓冲区满时放入数据,消费者也不会在缓冲区空时消耗数据。不够完善的解决方法容易出现死锁的情况,此时进程都在等待唤醒。

Java 实例 - 生产者/消费者问题

  • 生产者:负责生产数据的模块
  • 消费者:负责处理数据的模块
  • 缓存区:消费者不能直接使用生产者的数据,他们之前有个数据存储的缓存区

解决生产者消费者问题

1、管理法(利用缓冲区解决)

public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        new Producer(synContainer).start();
        new Consumer(synContainer).start();
    }
}
class Producer extends Thread{
    SynContainer synContainer;
    public Producer(SynContainer synContainer){
        this.synContainer = synContainer;
    }
    @Override
    public void run() {
        //生产产品
        for (int i = 0; i < 40; i++) {
            System.out.println("生产了第" + (i+1) + "只鸡");
            synContainer.push(new Chicken(i+1));
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Consumer extends Thread{
    SynContainer synContainer;
    public Consumer(SynContainer synContainer){
        this.synContainer = synContainer;
    }
    @Override
    public void run() {
        //消费产品
        for (int i = 0; i < 100; i++) {
            Chicken chicken = synContainer.pop();
            System.out.println("消费了第" + chicken.id + "只鸡");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Chicken{
    int id;//产品编号
    public Chicken(int id){
        this.id = id;
    }
}
//缓冲区
class SynContainer{
    //缓冲区大小
    Chicken[] ckList = new Chicken[10];

    int count = 0;
    //生产者放入产品
    public synchronized void push(Chicken chicken){
        if(count == ckList.length - 1){//缓冲区已经满了
            //通知消费者使用,生产者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        ckList[count] = chicken;
        count ++;
        this.notifyAll();
    }
    //消费者取走产品
    public synchronized Chicken pop(){
        if(count == 0){//缓冲区已经空了
            //通知生产者生产,消费者等待
            try {
                this.notifyAll();
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count --;
        return ckList[count];
    }
}

2、信号灯法

public class TestPc2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生产者---->演员
class Player extends Thread{
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2 == 0){
                this.tv.play("快乐大本营播放中");
            }else {
                this.tv.play("抖音记录美好生活");
            }
        }
    }
}

//消费者---->观众
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.tv.watch();
        }
    }
}
//产品---->节目
class TV{
    String voice;
    boolean flag = true;

    //生产者放入产品
    public synchronized void play(String voice){
        if(!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了:" + voice);
        this.notifyAll();//通知观众观看
        this.voice = voice;
        this.flag = !this.flag;

    }
    //生产者放入产品
    public synchronized void watch(){
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了:" + voice);
        this.notifyAll();//通知观众观看
        this.voice = voice;
        this.flag = !this.flag;
    }
}
线程池

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大

思路:可以创建多个线程,放入线程池中,使用时直接获取,使用完放回线程池,可以避免频繁创建和销毁,实现重复利用

好处:

  • 提高响应速度

  • 降低资源消耗

  • 便于线程管理

    corePoolSize:核心池大小

    maximumPoolSize:最大线程数

    keepAliveTime:线程没有任务时最多保持多久会停止

接口:Executor,CompletionService,ExecutorService,ScheduledExecutorService

抽象类:AbstractExecutorService

实现类:ExecutorCompletionService,ThreadPoolExecutor,ScheduledThreadPoolExecutor

class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        //创建线程池,newFixedThreadPool 参数为线程池大小
        ExecutorService pool = Executors.newFixedThreadPool(4);
        //执行
        pool.execute(new MyThread());
        pool.execute(new MyThread());
        pool.execute(new MyThread());
        pool.execute(new MyThread());
        pool.execute(new MyThread());
        pool.execute(new MyThread());

        //关闭链接
        pool.shutdown();
    }
}

结果:

pool-1-thread-2
pool-1-thread-2
pool-1-thread-3
pool-1-thread-1
pool-1-thread-4
pool-1-thread-2
posted @ 2021-05-08 20:38  莫逆追风  阅读(48)  评论(1)    收藏  举报