• Day18_进程(中)


    Day18_进程(中)

    常用的一些方法

    join():半路杀出程咬金

    sleep(1000):线程停止1000ms。

    yield():暂停当前线程,使当前线程转入就绪阶段

    stop():过期方法

    伴随线程

    public class Bansui extends Thread{
        @Override
        public void run() {
            for (int i = 1; i <=1000 ; i++) {
                System.out.println("Bansui----"+i);
            }
        }
    
        public static void main(String[] args) {
            for (int i = 1; i <=10 ; i++) {
                if(i==6){
                    Bansui bs=new Bansui();
                    //设置伴随线程,参数设置为true,实现"同生共死",但是两个线程停止时间点会有时间间隔
                    bs.setDaemon(true);
                    bs.start();
                }
                System.out.println("main---"+i);
            }
        }
    }
    

    输出

    main---1
    main---2
    main---3
    main---4
    main---5
    main---6
    main---7
    main---8
    main---9
    Bansui----1
    main---10
    Bansui----2
    Bansui----3
    Bansui----4
    Bansui----5
    Bansui----6
    Bansui----7
    Bansui----8
    Bansui----9
    Bansui----10
    Bansui----11
    Bansui----12
    Bansui----13
    

    线程的同步

    加锁

    同步代码块

    public class Ticket implements Runnable{
        private int ticketNum=10;
        @Override
        public void run() {
            for (int i = 1; i <=100; i++) {
              //同步代码块
              //加锁
                synchronized (Ticket.class){
                    if(ticketNum>0){
                        System.out.println("我在"+Thread.currentThread().getName()+"买到了剩余的第"+ticketNum--+"张火车票!");
                    }
                }
            }
        }
    }
    
    public class ticketTest {
        public static void main(String[] args) {
            Ticket tk=new Ticket();
            Thread t1=new Thread(tk,"窗口1");
            t1.start();
            Thread t2=new Thread(tk,"窗口2");
            t2.start();
            Thread t3=new Thread(tk,"窗口3");
            t3.start();
        }
    }
    

    输出:

    我在窗口1买到了剩余的第10张火车票!
    我在窗口1买到了剩余的第9张火车票!
    我在窗口1买到了剩余的第8张火车票!
    我在窗口1买到了剩余的第7张火车票!
    我在窗口1买到了剩余的第6张火车票!
    我在窗口1买到了剩余的第5张火车票!
    我在窗口1买到了剩余的第4张火车票!
    我在窗口1买到了剩余的第3张火车票!
    我在窗口1买到了剩余的第2张火车票!
    我在窗口1买到了剩余的第1张火车票!
    

    同步方法

    public class Ticket implements Runnable{
        private int ticketNum=10;
        @Override
        public void run() {
            for (int i = 1; i <=100 ; i++) {
                buy();
            }
        }
      //同步方法
        public synchronized void buy(){
            if(ticketNum>0){
                System.out.println("我在"+Thread.currentThread().getName()+"买到了剩余的第"+ticketNum--+"张火车票!");
            }
        }
    }
    
    public class ticketTest {
        public static void main(String[] args) {
          //同一个tk对象,所以一把锁即可实现同步
            Ticket tk=new Ticket();
            Thread t1=new Thread(tk,"窗口1");
            t1.start();
            Thread t2=new Thread(tk,"窗口2");
            t2.start();
            Thread t3=new Thread(tk,"窗口3");
            t3.start();
        }
    }
    
    我在窗口1买到了剩余的第10张火车票!
    我在窗口1买到了剩余的第9张火车票!
    我在窗口1买到了剩余的第8张火车票!
    我在窗口1买到了剩余的第7张火车票!
    我在窗口1买到了剩余的第6张火车票!
    我在窗口1买到了剩余的第5张火车票!
    我在窗口3买到了剩余的第4张火车票!
    我在窗口3买到了剩余的第3张火车票!
    我在窗口3买到了剩余的第2张火车票!
    我在窗口3买到了剩余的第1张火车票!
    

    总结

    synchronized

    1. 必须是引用数据类型,不能是基本数据类型
    2. 在同步代码块中可以改变同步监视器对象的值,不能改变其引用
    3. 尽量不要String和包装类Integer做同步监视器。如果使用了,只要保证代码块不对其进行任何操作也没关系
    4. 一般使用共享资源做同步监视器即可
    5. 也可以创建一个专门的同步监视器,没有任何业务含义
    6. 建议使用final修饰同步监视器

    同步代码块的执行过程

    1. 第一个线程来到同步代码块,发现同步监视器open状态,需要close,然后执行其中的代码
    2. 第一个线程执行过程中,发生了线程切换(阻塞 就绪),第一个线程失去CPU,但是没有开锁open
    3. 第二个线程获取了CPU,来到了同步代码块,发现同步监视器close状态,无法执行其中的代码,第二个线程也进入阻塞状态
    4. 第一个线程再次获取了CPU,接着执行了后续的代码,同步代码块执行完毕,释放锁open
    5. 第二个线程也再次获取CPU,来到了同步代码块,发现同步监视器open状态,重复第一个线程的处理过程(加锁)。强调:同步代码块中能发生线程切换吗?能!!但是后续的被执行的线程也无法执行同步代码块(锁仍旧close;

    线程同步优缺点

    优点:安全

    缺点:效率低下,可能出现死锁

    第三种加锁方式Lock

    Lock lock=new ReentrantLock();
    //加锁
    lock.lock();
    try{
      //要锁的代码
    }finally{
      //解除锁
      lock.unlock();
    }
    

    Lock更加灵活,但是只能使用同步块,不能用同步方法。

    线程的通信

    锁池:synchronized

    等待池:wait(),notify(),notifyAll()

    如果一个线程调用了某个对象的wait()方法,那么该线程进入到该对象的等待池中(并且已经将锁释放),如果未来的某个时刻,另外一个线程调用了相同对象的notify方法或者notifyAll方法,那么该等待池中的线程就会被唤起,然后进入到对象的锁池里面去获得该对象的锁,如果获得锁成功后,那么该线程就会沿着wait方法之后的语句继续执行,注意是沿着wait方法之后的语句继续执行。

    public class Shangpin {
        private String name;
        private String brand;
        //信号灯
        boolean flag=false;
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getBrand() {
            return brand;
        }
    
        public void setBrand(String brand) {
            this.brand = brand;
        }
    
        //生产者
        public synchronized void setSp(String brand,String name){
            //有商品,生产者等待
            if(flag){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            this.setBrand(brand);
            this.setName(name);
            System.out.println("生产者生产了"+this.getBrand()+"---"+this.getName());
            //生产出来了,通知消费者来取产品
            flag=true;
            notify();
        }
        //消费者
        public synchronized void getSp() {
            //无商品,消费者等待
            if (!flag) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("消费者消费了" + this.getBrand() + "---" + this.getName());
            //消费者取完产品,通知生产者生产
            flag = false;
            notify();
        }
    }
    
    public class Shengchanzhe implements Runnable {
        private Shangpin sp;
    
        public Shengchanzhe(Shangpin sp) {
            this.sp = sp;
        }
    
        @Override
        public void run() {
            //为什么要加for循环,因为要生产10个商品,每生产一个商品,就会停下来等待消费者消费商品
            //可以尝试把消费者中的for循环删除,如果能解释结果,就说明对线程的运行有所了解了
            //原因是:没加for循环时,消费者只消费了一次,所以线程就停在那里了
            for (int i = 1; i <=10 ; i++) {
                if(i%2==0){
                    sp.setSp("DOVE","chocolate");
                }else{
                    sp.setSp("青岛","啤酒");
                }
            }
        }
    }
    
    public class Xiaofeizhe implements Runnable{
        private Shangpin sp;
    
        public Xiaofeizhe(Shangpin sp) {
            this.sp = sp;
        }
    
        @Override
        public void run() {
            for (int i = 1; i <=10 ; i++) {
                sp.getSp();
            }
        }
    }
    
    public class ShangpinTest {
        public static void main(String[] args) {
            Shangpin sp=new Shangpin();
            Shengchanzhe scz=new Shengchanzhe(sp);
            new Thread(scz).start();
    
            Xiaofeizhe xfz=new Xiaofeizhe(sp);
            new Thread(xfz).start();
        }
    }
    

    输出

    生产者生产了青岛---啤酒
    消费者消费了青岛---啤酒
    生产者生产了DOVE---chocolate
    消费者消费了DOVE---chocolate
    生产者生产了青岛---啤酒
    消费者消费了青岛---啤酒
    生产者生产了DOVE---chocolate
    消费者消费了DOVE---chocolate
    生产者生产了青岛---啤酒
    消费者消费了青岛---啤酒
    生产者生产了DOVE---chocolate
    消费者消费了DOVE---chocolate
    生产者生产了青岛---啤酒
    消费者消费了青岛---啤酒
    生产者生产了DOVE---chocolate
    消费者消费了DOVE---chocolate
    生产者生产了青岛---啤酒
    消费者消费了青岛---啤酒
    生产者生产了DOVE---chocolate
    消费者消费了DOVE---chocolate
    
  • 相关阅读:
    element-ui日期筛选:选择日期即触发查询
    js点击按钮复制内容到粘贴板
    axios配置及使用(发起请求时带上token)
    axios + vue导出excel文件
    textarea与标签组合,点击标签填入标签内容,再次点击删除内容(vue)
    vue复制textarea文本域内容到粘贴板
    ElementUI动态表格数据转换formatter
    elementUI图片墙上传
    高德地图模糊搜索地址(elementUI)
    elementUI表单验证
  • 原文地址:https://www.cnblogs.com/gaoyao/p/13624570.html
Copyright © 2020-2023  润新知