简述Java线程(二)

本人小白一枚,欢迎大家一起讨论学习,如有错误,还望大家指教。

线程状态概述:

在这里我们叙述下在线程生命周期中,线程存在的几种状态。
在这里插入图片描述
详细可以参考下图:
在这里插入图片描述
我们在这里可以思考下为什么操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中的线程中,都必须要表示它们所操作线程持有的锁,只有同一个锁上的被等待想成,可以被同一个锁上的notify唤醒。不可以对不同锁中的线程进行唤醒,而锁可以是任意对象,可以被任意对象调用的方法定义在Object中。
在这里探讨下死锁情况,当同步中嵌套同步时,可能出现死锁情况,代码如下:

public class MainDemo {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        
        thread1.start();
        ticket.flag = false;
        thread2.start();
    }
}

class Ticket implements Runnable {
    private int ticket = 100;
    Object lock = new Object();
    boolean flag = true;

    @Override
    public void run() {
        if (flag) {
            while (true) {
                synchronized (lock) {
                    sellTicket();
                }

            }
        } else {
            while (true) {
                sellTicket();
            }
        }

    }
    private synchronized void sellTicket() {
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在卖:" + ticket--);
        }
    }
}

线程等待唤醒机制:

简述: 多个线程在处理同一个资源,但是处理的动作却不相同。比如:线程A用来做包子,线程B用来吃包子,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,当A线程生产好了,通知B来消费,此时线程A与线程B就存在线程通信问题了。这种机制的出现解决了当我们希望多个线程有规律的执行时,此时多线程之间就需要一些协调通信,以此达到我们多线程共同操作同一份数据的目的。
等待唤醒中的方法:
wait():线程不再活动,不再参与调度,进入wait set中,因此不会浪费CPU资源,也不会去竞争锁了,这时的线程状态即是WAITING。它还要等待别的线程执行一个特别的动作,也即是 “通知(notify)” 在这个对象上等待的线程从wait set中释放出来,重新进入到调度队列中。
notify():选取所通知对象的wait set中的一个线程释放;例如餐馆有空位置时,排在前面的顾客最先入座。
notifyAll():释放所通知对象的wait set上的全部线程。
注意一: 当我们唤醒一个等待的线程,被唤醒的线程不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以它需要再次竞争锁,成功后才能在当初调用wait方法之后的地方恢复执行。
注意二: wait方法与notify方法必须要由同一个锁对象调用。因为对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
注意三: wait方法与notify方法必须要在同步代码块或者同步函数中使用,因为:必须要通过锁对象调用这2个方法。

案例:

public class MainDemo {
    public static void main(String[] args) {
        // 测试类
        BaoZi bz = new BaoZi();

        ChiHuo chiHuo = new ChiHuo(bz);
        BaoZiPu baoZiPu = new BaoZiPu(bz);

        chiHuo.start();
        baoZiPu.start();
    }
}

class BaoZi {
    String pier;
    String xianer;
    boolean flag = false;
}

class ChiHuo extends Thread {
    private BaoZi bz;

    ChiHuo(BaoZi bz) {
        this.bz = bz;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (bz) {
                if (!bz.flag) {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("吃货正在吃" + bz.pier + bz.xianer);
                bz.flag = false;
                bz.notify();
            }
        }

    }
}

class BaoZiPu extends Thread {
    private BaoZi bz;

    BaoZiPu(BaoZi bz) {
        this.bz = bz;
    }

    @Override
    public void run() {
        int count = 0;
        while (true) {
            synchronized (bz) {
                if (bz.flag) {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (count > 1) {
                    break;
                }
                System.out.println("包子铺开始做包子");
                if (count == 0) {
                    bz.pier = "冰皮";
                    bz.xianer = "五仁";
                } else {

                    bz.pier = "薄皮";
                    bz.xianer = "牛肉大葱";
                }
                count++;
                bz.flag = true;
                System.out.println("包子做好了:" + bz.pier + bz.xianer);
                bz.notify();
            }
        }
    }
}

打印结果:

包子铺开始做包子
包子做好了:冰皮五仁
吃货正在吃冰皮五仁
包子铺开始做包子
包子做好了:薄皮牛肉大葱
吃货正在吃薄皮牛肉大葱

线程池:

简述: 就是一个可以容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,从而大大减小了反复创建线程而消耗的过多资源。
在这里插入图片描述
使用线程池的好处:

  • 降低资源消耗,减少了创建和销毁线程的次数,每个工作线都可以被重复被利用,可执行多个任务。
  • 提高响应速度。当任务到达时,任务可以不需要的等待线程创建而是能立即执行。
  • 提高线程的可管理性,可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,从而导致服务器死机(每个线程需要大学1MB内存)。

线程池的使用:
Java里面线程池的顶级接口是java.util.concurrent.Executor,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优 的,因此在java.util.concurrent.Executors线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官方建议使用Executors工程类来创建线程池对象。

使用Executors类中创建线程池的方法:

  • public static ExecutorService newFixedThreadPool(int nThreads) :返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)。
  • public Future<?> submit(Runnable task) :获取线程池中某一个线程对象,并执行。
    • Future接口:用来记录线程任务执行完毕后产生的结果,线程池创建与使用。

案例:

public class MainDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        Runnable myRunnable = new MyRunnable();
        executorService.submit(myRunnable);
        executorService.submit(myRunnable);
        //这里我们创建了一个容量为2的线程池,在这里我们调用了三次,所以我们需要等待前两个执行完,在从池中取出
        executorService.submit(myRunnable);

    }
}

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

打印结果

pool-1-thread-2
pool-1-thread-1
pool-1-thread-1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值