zoukankan      html  css  js  c++  java
  • JAVA学习day09

    线程状态

    线程状态
    线程状态2

    线程方法

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

    停止线程

    • 不推荐JDK提供的stop(),destory()方法(已废弃)。
    • 推荐线程自己停下来
    • 建议使用一个标志位进行终止变量,当flag=false终止线程运行。

    TestStop

    //测试stop
    //1.建议线程正常停止--->利用次数,不建议死循环,
    //2.建议使用标志位--->设置一个标志位
    //3.不要使用stop或者destory等过时或者JDK不建议使用的方法
    public class TestStop implements Runnable{
        
        //1.设置一个标志位
        private boolean flag = true;
        
        @Override
        public void run() {
            int i = 0;
            while(flag){
                System.out.println("run...Thread"+i++);
            }
        }
        
        //2.设置一个公开的方法停止线程,转换标志位
        
        public void stop(){
            this.flag = false;
        }
    
        public static void main(String[] args) {
            TestStop testStop = new TestStop();
            
            new Thread(testStop).start();
    
            for (int i = 0; i < 1000; i++) {
                //不加这一句线程根本来不及跑
                System.out.println("main"+i);
                if (i==900){
                    //调用stop切换标志位让线程停止
                    testStop.stop();
                    System.out.println("线程该停止了");
                }
            }
        }
    }
    
    

    线程休眠

    • sleep(时间)指定线程阻塞的毫秒数;
    • sleep存在异常InterruptedException;
    • sleep时间达到后线程进入就绪状态;
    • sleep可以模拟网络延时,倒计时等;
    • 每个对象都有一个锁,sleep不会释放锁

    TestSleep2

    public class TestSleep2 {
    
        public static void main(String[] args) {
    //        try {
    //            tenDown();
    //        } catch (InterruptedException e) {
    //            e.printStackTrace();
    //        }
    
            Date startTime ;
    
            while (true){
                try {
                    startTime = new Date(System.currentTimeMillis());//更新当前时间
                    System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    
        public static void tenDown() throws InterruptedException {
            int num = 10;
    
            while (true){
                Thread.sleep(1000);
                System.out.println(num--);
                if(num<=0){
                    break;
                }
            }
        }
    }
    
    

    线程礼让Yield

    • 礼让线程,让当前正在执行的线程暂停,但不阻塞
    • 将线程从运行状态转为就绪状态
    • 让CPU重新调度,礼让不一定成功!看CPU心情

    TestYield

    //测试礼让线程
    //礼让不一定成功,看CPU心情
    public class TestYield {
        public static void main(String[] args) {
            MyYield myYield = new MyYield();
    
            new Thread(myYield,"a").start();
            new Thread(myYield,"b").start();
        }
    }
    
    
    class MyYield implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"线程开始执行");
            Thread.yield();//礼让
            System.out.println(Thread.currentThread().getName()+"线程停止执行");
        }
    }
    
    

    Join

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

    TestJoin

    public class TestJoin implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                System.out.println("线程VIP来了"+i);
            }
        }
    
    
        public static void main(String[] args) throws InterruptedException {
            //启动线程
            TestJoin testJoin = new TestJoin();
            Thread thread = new Thread(testJoin);
            thread.start();
    
            //主线程
            for (int i = 0; i < 1000; i++) {
                if(i==200){
                    thread.join();//插队
                }
                System.out.println("main"+i);
    
            }
        }
    }
    
    

    线程状态

    • Thread.State
      查看JDK帮助文档,搜索Thread:state
      • NEW
        尚未启动的线程处于此状态
      • RUNNABLE
        在Java虚拟机中执行的线程处于此状态
      • BLOCKED
        被阻塞等待监视器锁定的线程出于此状态
      • WAITING
        正在等待另一个线程执行特定动作的线程处于此状态
      • TIMED_WAITING
        正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
      • TERMINATED
        已退出的线程出于此状态
        一个线程可以在给定时间点处于一个状态。这些状态是不反应任何操作系统线程状态的虚拟机状态。

    TestState

    //观察测试线程的状态
    public class TestState {
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(()->{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
                }
                System.out.println("////////");
            });
    
            //观察状态
            Thread.State state = thread.getState();
            System.out.println(state);//NEW
    
            //观察启动后
            thread.start();
            state = thread.getState();
            System.out.println(state);
    
            while(state != Thread.State.TERMINATED){
                //主线程sleep 100ms,所以就是每隔100ms运行一次while里的代码
                Thread.sleep(100);
                state=thread.getState();
                System.out.println(state);
            }
    
            //死亡后的线程不可再运行,所以下面这句会报错
            thread.start();
        }
    }
    
    

    运行结果
    NEW
    RUNNABLE
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    TIMED_WAITING
    ////////
    TERMINATED
    Exception in thread "main"

    线程优先级

    • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该先调度哪个线程来执行
    • 线程的优先级用数字表示,范围1~10
      • Thread.MIN_PRIORITY = 1;
      • Thread.MAX_PRIORITY = 10;
      • Thread.NORM_PRIORITY = 5;
    • 用以下方式改变或获取优先级
      • getPriority().setPriority(int xxx)

    TestPriority

    //测试线程的优先级
    public class TestPriority extends Thread{
    
        public static void main(String[] args) {
            //主线程默认优先级
            System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
    
            MyPriority myPriority = new MyPriority();
    
            Thread t1 = new Thread(myPriority,"t1");
            Thread t2 = new Thread(myPriority,"t2");
            Thread t3 = new Thread(myPriority,"t3");
            Thread t4 = new Thread(myPriority,"t4");
            Thread t5 = new Thread(myPriority);
            Thread t6 = new Thread(myPriority);
    
            //先设置优先级,再启动
            t1.start();
    
            t2.setPriority(1);
            t2.start();
    
            t3.setPriority(4);
            t3.start();
    
            t4.setPriority(MAX_PRIORITY); //MAX_PRIORITY = 10
            t4.start();
    
    //        t5.setPriority(-1);
    //        t5.start();
    
    //        t6.setPriority(11);
    //        t6.start();
    
            }
    }
    
    class MyPriority implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
        }
    }
    
    

    运行结果随机变化
    优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了。都是看CPU的调度。同理优先级高也可可能被后调度。

    守护(daemon)线程

    • 线程分为用户线程守护线程
    • 虚拟机必须确保用户线程执行完毕
    • 虚拟机不用等待守护线程执行完毕
    • 如,后台记录操作日志,监控内存,垃圾回收等待

    TestDaemon

    //测试守护线程
    //上帝守护你
    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表示是用户线程,正常的线程都是用户线程
    
            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("你一生都开心地活着"+i);
            }
            System.out.println("====goodbye!world!");
        }
    }
    
    

    运行结果:当you线程运行完后,god线程只再持续了一小会儿,整个程序就全部终止运行了。

    线程同步

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

    • 由于统一进程的多个线程共享同一块存储空间,再带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其它线程必须等待,使用后释放锁即可,存在以下问题:
      • 一个线程持有锁会导致其它所有需要次锁的线程挂起;
      • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
      • 如果一个优先级高的i雄安承等待一个优先级低的线程释放锁,回导致优先级倒置,引起性能问题

    不安全的三个例子

    UnsafeBuyTicket

    //不安全的买票
    //线程不安全,有负数
    public class UnsafeBuyTicket {
    }
    
    class BuyTicket implements Runnable{
    
        public static void main(String[] args) {
            BuyTicket station = new BuyTicket();
    
            new Thread(station,"明").start();
            new Thread(station,"红").start();
            new Thread(station,"亮").start();
        }
    
    
        //票
        private int ticketNums = 10;
        boolean flag = true;//外部停止方式
    
    
        @Override
        public void run() {
            //买票
            while(flag){
                buy();
            }
    
        }
    
        private void buy(){
            //判断是否有票
            if(ticketNums<=0){
                flag = false;
                return;
            }
            //模拟延时
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //买票
            System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
        }
    }
    
    

    运行结果:

    红拿到10
    明拿到10
    亮拿到10
    亮拿到9
    红拿到7
    明拿到8
    亮拿到6
    红拿到5
    明拿到4
    亮拿到3
    红拿到2
    明拿到3
    明拿到1
    亮拿到0
    红拿到-1

    UnsafeBank

    //不安全的银行
    //两个人去银行取钱,账户
    public class UnsafeBank {
        public static void main(String[] args) {
            //账户
            Account account = new Account(100,"结婚基金");
    
            Drawing you = new Drawing(account,50,"你");
            Drawing girlfriend = new Drawing(account,100,"她");
    
            you.start();
            girlfriend.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;
            }
    
            //sleep可以放大问题的发生性
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //卡内余额 = 余额 - 你取的钱
            account.money = account.money - drawingMoney;
            nowMoney = nowMoney + drawingMoney;
    
            System.out.println(account.name+"余额为:"+account.money);
            //此处Thread.currentThread().getName()等价于this.getName()
            System.out.println(this.getName()+"手里的钱:"+nowMoney);
        }
    }
    
    

    运行结果:
    结婚基金余额为:-50
    结婚基金余额为:-50
    你手里的钱:50
    她手里的钱:100

    UnsafeList

    //线程不安全的集合
    public class UnsafeList {
        public static void main(String[] args) {
            List<String > list = new ArrayList<>();
            for (int i = 0; i < 10000; i++) {
                new Thread(()->{
                    list.add(Thread.currentThread().getName());
                }).start();
            }
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(list.size());
        }
    }
    
    

    运行结果:
    9998
    如果不加sleep,运行结果:
    9899

    同步方法

    • 由于可以通过private关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,通常包括两种用法:synchronized方法和synchronized块
      同步方法:public synchronized void method(int args){}
    • synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得该调用方法的对象的锁才能执行,否则就会线程阻塞,方法一旦执行就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
      缺陷:若将一个大的方法申明为synchronized将会影响效率

    同步块

    • 同步块:synchronized(Obj){}
    • Obj称之为同步监视器
      • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
      • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class[反射中讲解]
    • 同步监视器的执行过程
      1. 第一个线程访问,锁定同步监视器,执行其中代码。
      2. 第二个线程访问,发现同步监视器被锁定,无法访问。
      3. 第一个线程访问完毕,解锁同步监视器。
      4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
        锁的对象就是变化的量,需要增删改的对象

    以上文中UnsafeBank举例

    只需修改run()方法

    //取钱
        //synchronized默认锁的是this.
        @Override
        public void run() {
            //锁的对象就是变化的量,需要增删改的对象
            synchronized (account) {
                //判断有没有钱
                if(account.money-drawingMoney<0){
                    System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
                    return;
                }
    
                //sleep可以放大问题的发生性
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //卡内余额 = 余额 - 你取的钱
                account.money = account.money - drawingMoney;
                nowMoney = nowMoney + drawingMoney;
    
                System.out.println(account.name+"余额为:"+account.money);
                //此处Thread.currentThread().getName()等价于this.getName()
                System.out.println(this.getName()+"手里的钱:"+nowMoney);
            }
        }
    
    

    这样就保证安全。

    最后一个不安全的列表,在将list加入synchronized块后,删去Thread.sleep(3000)输出出来的结果依然可能不是1000。这是由于run线程还未执行完毕而主线程已经执行到输出list.size()导致的,如果在run线程中加入输出语句,就可以看见在main已经输出完毕后,依旧有新的线程在输出。所以仍然是安全的,只是执行顺序有问题。

    JUC安全类型集合

    JUC:java.util.concurrent 就是并发包

    TestJUC

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

    结果:
    10000

    死锁

    • 多个线程各自占有一些共享资源,并且互相都等待其它线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况。某一个同步块同时拥有“两个以上对象的锁”时,就可能发生“死锁”的问题。
    • 四个必要条件:
      1、 互斥: 某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
      2、 占有且等待: 一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
      3、 不可抢占: 别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
      4、 循环等待: 存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
      破坏四个条件中的一个就可以避免死锁

    DeadLock

    //死锁:多个线程互相抱着对方的资源,然后形成僵持
    public class DeadLock {
        public static void main(String[] args) {
            Makeup g1 = new Makeup(0,"灰姑娘");
            Makeup g2 = new Makeup(1,"白雪公主");
    
            g1.start();
            g2.start();
        }
    }
    
    //口红
    class Lipstick{
    
    }
    
    //镜子
    class Mirror{
    
    }
    
    //化妆
    class Makeup extends Thread {
    
        //需要的资源只有一份,用static保证只有一份
        static Lipstick lipstick = new Lipstick();
        static Mirror mirror = new Mirror();
    
        int choise; //选择
        String girlName;//选择化妆品的人
    
        Makeup(int choise, String name) {
            this.choise = choise;
            this.girlName = name;
        }
    
        @Override
        public void run() {
            //化妆
            try {
                makeup();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
    
        //化妆,互相持有对方的锁,就是需要拿到对方的资源
        private void makeup() throws InterruptedException {
            if (choise == 0) {
                synchronized (lipstick) {//获得口红的锁
                    System.out.println(this.girlName + "获得口红的锁");
                    Thread.sleep(1000);
    
                    synchronized (mirror) {//一秒钟后想获得镜子
                        System.out.println(this.girlName + "获得镜子的锁");
    
                    }
                }
    
            } else {
                synchronized (mirror) {//获得口红的锁
                    System.out.println(this.girlName + "获得镜子的锁");
                    Thread.sleep(2000);
    
                    synchronized (lipstick) {//两秒钟后想获得口红
                        System.out.println(this.girlName + "获得口红的锁");
                    }
                }
            }
        }
    }
    
    

    运行结果:
    灰姑娘获得口红的锁
    白雪公主获得镜子的锁
    然后构成死锁,程序卡住

    解决方法:
    破坏四个必要条件中的一个,比如DeadLock代码中,可以将makeup()改成

    private void makeup() throws InterruptedException {
            if (choise == 0) {
                synchronized (lipstick) {//获得口红的锁
                    System.out.println(this.girlName + "获得口红的锁");
                    Thread.sleep(1000);
    
    
                }
                synchronized (mirror) {//一秒钟后想获得镜子
                    System.out.println(this.girlName + "获得镜子的锁");
    
                }
    
            } else {
                synchronized (mirror) {//获得口红的锁
                    System.out.println(this.girlName + "获得镜子的锁");
                    Thread.sleep(2000);
    
    
                }
                synchronized (lipstick) {//两秒钟后想获得口红
                    System.out.println(this.girlName + "获得口红的锁");
                }
            }
    

    这样他们不会抱互相的锁

    Lock(锁)

    • 从JDK5.0开始,Java提供了更强大的线程同步机制————通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
    • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源前应获得Lock对象
    • ReentrantLock(可重入锁)类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的就是ReentrantLock,可以显式加锁、释放锁。

    TestLock

    public class TestLock {
        public static void main(String[] args) {
            TestLock2 testLock2 = new TestLock2();
    
            new Thread(testLock2).start();
            new Thread(testLock2).start();
            new Thread(testLock2).start();
        }
    }
    
    
    class TestLock2 implements Runnable{
    
        int ticketNums = 10;
    
        //定于lock锁
        private final ReentrantLock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while(true){
    
                try{
                    //加锁
                    lock.lock();
                    if(ticketNums>0){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(ticketNums--);
                    }else{
                        break;
                    }
                }finally {
                    //解锁
                    lock.unlock();
                }
            }
        }
    }
    
    

    如果同步代码有异常,要将unlock()写入finally语句块中。finally一定会执行

    synchronized和Lock对比

    • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
    • Lock只有代码块锁,synchronized有代码块锁和方法锁
    • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的拓展性(提供更多的子类)
    • 优先使用顺序:
      • Lock>同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)0

    线程协作

    线程通信

    • 应用场景:生产者和消费者问题
      • 假设仓库中只能存放一件产品﹐生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费.
      • 如果仓库中没有产品﹐则生产者将产品放入仓库﹐否则停止生产并等待,直到仓库中的产品被消费者取走为止.
      • 如果仓库中放有产品﹐则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止.

    分析

    这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件.

    • 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又需要马上通知消费者消费
    • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费.
    • 在生产者消费者问题中﹐仅有synchronized是不够的
      • synchronized可阻止并发更新同一个共享资源,实现了同步
      • synchronized不能用来实现不同线程之间的消息传递(通信)

    线程通信

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

    注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常lllegalMonitorStateException

    解决方法1

    并发协作模型“生产者/消费者模式”--->管程法

    • 生产者:负责生产数据的模块(可能是方法﹐对象﹐线程﹐进程);
    • 消费者︰负责处理数据的模块(可能是方法﹐对象,线程﹐进程);
    • 缓冲区∶消费者不能直接使用生产者的数据﹐他们之间有个“缓冲区”
      生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

    TestPC

    
    //测试生产者消费者模型--->利用缓冲区解决:管程法
    //生产者,消费者,产品,缓冲区
    public class TestPC {
        public static void main(String[] args) {
            SynContainer container = new SynContainer();
    
            new Producer(container).start();
            new Consumer(container).start();
    
        }
    }
    
    //生产者
    class Producer extends Thread{
        SynContainer container;
    
        public Producer(SynContainer container){
            this.container = container;
        }
        //生产
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
    
                container.push(new Chicken(i));
                System.out.println("生产了"+i+"只鸡");
            }
        }
    }
    
    //消费者
    class Consumer extends Thread{
        SynContainer container;
    
        public Consumer(SynContainer container){
            this.container = container;
        }
        //消费
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("消费了-->"+container.pop().id+"只鸡");
            }
        }
    }
    
    //产品
    class Chicken{
        int id;//产品编号
    
        public Chicken(int id) {
            this.id = id;
        }
    
    }
    
    //缓冲区
    class SynContainer{
    
        //需要一个容器大小
        Chicken[] chickens = new Chicken[10];
        int count = 0;
    
        //生产者放入产品
        public synchronized void push(Chicken chicken){
            //如果容器满了,就需要等待消费者消费
            if(count==chickens.length){
                //生产者等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            //如果没有满,就需要丢入产品
            chickens[count]=chicken;
            count++;
    
            //可以通知消费者消费了
            this.notifyAll();
    
        }
    
        //消费者消费产品
        public synchronized Chicken pop(){
            //判断能否消费
            if (count == 0){
                //等待生产者生产,消费者等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //如果可以消费
            count--;
            Chicken chicken = chickens[count];
    
            //吃完了通知生产者生产
            this.notifyAll();
            return chicken;
        }
    }
    
    

    运行结果:
    生产了0只鸡
    生产了1只鸡
    生产了2只鸡
    生产了3只鸡
    生产了4只鸡
    生产了5只鸡
    生产了6只鸡
    生产了7只鸡
    生产了8只鸡
    生产了9只鸡
    生产了10只鸡
    消费了-->9只鸡
    消费了-->10只鸡
    生产了11只鸡
    消费了-->11只鸡
    生产了12只鸡
    消费了-->12只鸡
    生产了13只鸡
    生产了14只鸡
    消费了-->13只鸡
    消费了-->14只鸡
    生产了15只鸡
    消费了-->15只鸡
    生产了16只鸡
    消费了-->16只鸡
    生产了17只鸡
    消费了-->17只鸡
    生产了18只鸡
    生产了19只鸡
    消费了-->18只鸡
    消费了-->19只鸡
    生产了20只鸡
    消费了-->20只鸡
    生产了21只鸡
    生产了22只鸡
    消费了-->21只鸡
    消费了-->22只鸡
    生产了23只鸡
    生产了24只鸡
    消费了-->23只鸡
    消费了-->24只鸡
    生产了25只鸡
    生产了26只鸡
    消费了-->25只鸡
    消费了-->26只鸡
    生产了27只鸡
    消费了-->27只鸡
    生产了28只鸡
    消费了-->28只鸡
    生产了29只鸡
    消费了-->29只鸡
    生产了30只鸡
    消费了-->30只鸡
    生产了31只鸡
    生产了32只鸡
    消费了-->31只鸡
    消费了-->32只鸡
    生产了33只鸡
    消费了-->33只鸡
    生产了34只鸡
    生产了35只鸡
    消费了-->34只鸡
    消费了-->35只鸡
    生产了36只鸡
    消费了-->36只鸡
    生产了37只鸡
    消费了-->37只鸡
    生产了38只鸡
    消费了-->38只鸡
    生产了39只鸡
    消费了-->39只鸡
    消费了-->40只鸡
    消费了-->8只鸡
    消费了-->7只鸡
    生产了40只鸡
    生产了41只鸡
    生产了42只鸡
    生产了43只鸡
    生产了44只鸡
    消费了-->6只鸡
    消费了-->44只鸡
    消费了-->45只鸡
    消费了-->43只鸡
    消费了-->42只鸡
    消费了-->41只鸡
    消费了-->5只鸡
    生产了45只鸡
    消费了-->4只鸡
    生产了46只鸡
    消费了-->46只鸡
    消费了-->47只鸡
    生产了47只鸡
    消费了-->3只鸡
    生产了48只鸡
    消费了-->48只鸡
    消费了-->49只鸡
    生产了49只鸡
    消费了-->2只鸡
    生产了50只鸡
    消费了-->50只鸡
    生产了51只鸡
    生产了52只鸡
    生产了53只鸡
    生产了54只鸡
    生产了55只鸡
    生产了56只鸡
    生产了57只鸡
    生产了58只鸡
    生产了59只鸡
    消费了-->51只鸡
    生产了60只鸡
    消费了-->59只鸡
    消费了-->60只鸡
    生产了61只鸡
    消费了-->61只鸡
    生产了62只鸡
    消费了-->62只鸡
    生产了63只鸡
    消费了-->63只鸡
    生产了64只鸡
    消费了-->64只鸡
    生产了65只鸡
    消费了-->65只鸡
    生产了66只鸡
    消费了-->66只鸡
    生产了67只鸡
    消费了-->67只鸡
    生产了68只鸡
    消费了-->68只鸡
    生产了69只鸡
    消费了-->69只鸡
    生产了70只鸡
    生产了71只鸡
    消费了-->70只鸡
    消费了-->71只鸡
    消费了-->58只鸡
    消费了-->57只鸡
    生产了72只鸡
    生产了73只鸡
    生产了74只鸡
    生产了75只鸡
    消费了-->72只鸡
    消费了-->75只鸡
    生产了76只鸡
    消费了-->76只鸡
    生产了77只鸡
    消费了-->77只鸡
    消费了-->78只鸡
    消费了-->74只鸡
    消费了-->73只鸡
    消费了-->56只鸡
    消费了-->55只鸡
    消费了-->54只鸡
    消费了-->53只鸡
    消费了-->52只鸡
    消费了-->1只鸡
    消费了-->0只鸡
    生产了78只鸡
    生产了79只鸡
    消费了-->79只鸡
    生产了80只鸡
    消费了-->80只鸡
    消费了-->81只鸡
    生产了81只鸡
    生产了82只鸡
    消费了-->82只鸡
    生产了83只鸡
    消费了-->83只鸡
    消费了-->84只鸡
    生产了84只鸡
    生产了85只鸡
    消费了-->85只鸡
    消费了-->86只鸡
    生产了86只鸡
    生产了87只鸡
    消费了-->87只鸡
    消费了-->88只鸡
    生产了88只鸡
    生产了89只鸡
    消费了-->89只鸡
    消费了-->90只鸡
    生产了90只鸡
    生产了91只鸡
    生产了92只鸡
    生产了93只鸡
    生产了94只鸡
    生产了95只鸡
    生产了96只鸡
    生产了97只鸡
    生产了98只鸡
    生产了99只鸡
    消费了-->91只鸡
    消费了-->99只鸡
    消费了-->98只鸡
    消费了-->97只鸡
    消费了-->96只鸡
    消费了-->95只鸡
    消费了-->94只鸡
    消费了-->93只鸡
    消费了-->92只鸡

    解决方法2

    • 并发协作模型“生产者/消费者模式”--->信号灯法

    TestPC2

    //测试生产者消费者问题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("声音1");
                }else {
                    this.tv.play("声音2");
                }
            }
        }
    }
    
    //消费者-->观众
    class Watcher extends Thread{
        TV tv;
        public Watcher(TV tv){
            this.tv = tv;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                tv.watch();
            }
        }
    }
    //产品-->节目
    class TV{
        //演员表演,观众等待 T
        //观众观看,演员等待 F
        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.flag=!this.flag;
        }
    
    }
    
    

    运行结果:
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2
    演员表演了声音1
    观看了声音1
    演员表演了声音2
    观看了声音2

    线程池

    • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
    • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
    • 好处:
      • 提高响应速度(减少了创建新线程的时间)
      • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
      • 便于线程管理(....)
        • corePoolSize:核心池的大小
        • maximumPoolSize:最大线程数
        • keepAliveTime:线程没有任务时最多保持多长时间后会终止

    使用线程池

    • JDK 5.0起提供了线程池相关APl: ExecutorServiceExecutors
    • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
      • void execute(Runnable command):执行任务/命令,没有返回值,一般用来技行Runnable
      • Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
      • void shutdown():关闭连接池
    • Executors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池

    TestPool

    //测试线程池
    public class TestPool {
        public static void main(String[] args) {
            //1.创建服务,创建线程池
            //newFixedThreadPool参数为:线程池大小
            ExecutorService service = Executors.newFixedThreadPool(10);
    
            //执行
            service.execute(new MyThread());
            service.execute(new MyThread());
            service.execute(new MyThread());
            service.execute(new MyThread());
    
            //2.关闭连接
            service.shutdown();
    
        }
    
    }
    
    class MyThread implements Runnable{
    
        @Override
        public void run() {
                System.out.println(Thread.currentThread().getName());
        }
    }
    
    
    

    运行结果:
    pool-1-thread-2
    pool-1-thread-1
    pool-1-thread-4
    pool-1-thread-3

    复习

    //回顾总结线程的创建
    public class ThreadNew {
        public static void main(String[] args) {
            new MyThread1().start();
    
            new Thread(new MyThread2()).start();
    
            FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
            new Thread(futureTask).start();
    
            try {
                Integer integer = futureTask.get();
                System.out.println(integer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    //1.继承Thread类
    class MyThread1 extends Thread{
        @Override
        public void run() {
            System.out.println("MyThread1");
        }
    }
    
    //2.实现Runnable接口
    class MyThread2 implements Runnable{
    
        @Override
        public void run() {
            System.out.println("MyThread2");
        }
    }
    
    //3.实现Callable接口
    class MyThread3 implements Callable<Integer>{
    
        @Override
        public Integer call() throws Exception {
            System.out.println("MyThread3");
            return 100;
        }
    }
    
    

    运行结果:
    MyThread1
    MyThread2
    MyThread3
    100

  • 相关阅读:
    GridView自定义分页
    intro
    ListView和DataPager初试
    在DataGrid中,如何录入数量及单价的时候自动算出金额
    常用正则表达式
    ASP.NET中基类页的设计和使用
    Asp.net实现无刷新检测用户名
    在asp.net2.0中使用存储过程
    .NET中的抽象工厂
    用C#生成随机中文汉字验证码的基本原理 [转]
  • 原文地址:https://www.cnblogs.com/ebym/p/15697283.html
Copyright © 2011-2022 走看看