• Java进阶


    1.线程方法

      (1)getName()获取线程名称

    主方法:
    public class DemoThread {
        public static void main(String[] args) {
            MyThread01 thread1 = new MyThread01();
            thread1.start();
            //获取主线程的名称
            System.out.println(Thread.currentThread().getName());
        }
    }
    
    线程子类:
    public class MyThread01 extends Thread {
        @Override
        public void run() {
            //获取线程名称方法1
            /*String name = getName();
            System.out.println(name);*/
            //获取线程名称方法2
            Thread t = Thread.currentThread();
            System.out.println(t.getName());
        }
    }

       (2)setName(name)设置线程名称

        可用线程调用setName方法设置名称;或者创建一个线程子类,并设置一个带参数的构造方法,并且调用父类的方法super(name);即可

      (3)sleep()使当前执行的线程暂停指定的毫秒数后再继续执行,需要捕捉异常

    public class DemoThread {
        public static void main(String[] args) {
            //模拟秒表
            for (int i = 1; i <= 60; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

     2.创建线程的第二种方式:实现runnable接口 并定义一个run的无参数方法  再创建一个Thread对象 将该实现类对象传递进去

    实现类:
    public class MyRunner implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }
    main方法:
    public class DemoThread {
        public static void main(String[] args) {
            MyRunner run1 = new MyRunner();
            Thread t = new Thread(run1);
            t.start();
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }

    两种开启线程方法的区别:

      实现runnable接口的好处:

        (1)避免了单继承的局限性

        (2)增强了程序的扩展性,降低了程序的耦合性

            把设置线程任务和开启新线程(经由Thread的对象传参执行)进行了分离(解耦)

    3.使用匿名内部类创建线程

    public class DemoThread {
        public static void main(String[] args) {
           //第一种方式
            new Thread(){
               @Override
               public void run() {
                   for (int i = 0; i < 20; i++) {
                       System.out.println(Thread.currentThread().getName() + i);
                   }
               }
           }.start();
            //第二种方式 接口实现
            Runnable r1 = new Runnable(){
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            };
            new Thread(r1).start();
            //简化方式
            new Thread(new Runnable(){
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            }).start();
        }
    }
    匿名内部类创建多线程

    4.线程安全问题,三个线程共抢一个共享资源,这种情况时不允许出现的

    实现类:
    public class MyRunner implements Runnable {
        private int ticket = 100;
        @Override
        public void run() {
            //模拟卖票
            while (true){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (ticket>0){
                    System.out.println(Thread.currentThread().getName() + "正在卖第" +ticket +"张票" );
                    ticket --;
                }
            }
        }
    }
    主线程:
    public class DemoThread {
        public static void main(String[] args) {
           MyRunner run = new MyRunner();
           //开启三个线程卖票
           Thread t1 = new Thread(run);
           Thread t2 = new Thread(run);
           Thread t3 = new Thread(run);
           t1.start();
           t2.start();
           t3.start();
        }
    }

     解决线程安全问题的三个方法

      (1)同步代码块

      (2)同步方法

      (3)锁机制

    (1)同步代码块

      格式:

        synchronized(锁对象){

          可能出现线程安全问题的代码块(访问了共享数据的代码)  

          }

    注意:1.同步代码块中的锁对象,可以是任何对象

          2.但是必须保证多个线程使用同一个锁对象

       3.锁对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行

    public class MyRunner implements Runnable {
        private int ticket = 100;
        //创建一个锁对象,必须在run方法外面,要保证该对象的同一性
        Object obj = new Object();
        @Override
        public void run() {
            //模拟卖票
            while (true){
                //创建同步代码块
                synchronized (obj){
                    if (ticket>0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在卖第" +ticket +"张票" );
                        ticket --;
                    }
                }
            }
        }
    }
    同步代码块

     原理:

     (2)同步方法:它也会把方法内部的代码块锁住,其锁对象就是实现类对象new出来的那个,也就是this

    实现类对象:
    public class ThreadSecurity implements Runnable {
        private int ticket = 100;
    
        @Override
        public void run() {
            while (true) {
                payTicket();
            }
        }
    
        public synchronized void payTicket() {
            //模拟卖票
            if (ticket > 0) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                ticket--;
            }
        }
    }
    主方法:
    public class DemoThread {
        public static void main(String[] args) {
           ThreadSecurity ts = new ThreadSecurity();
           //开启三个线程卖票
           Thread t1 = new Thread(ts);
           Thread t2 = new Thread(ts);
           Thread t3 = new Thread(ts);
           t1.start();
           t2.start();
           t3.start();
        }
    }
    同步方法

     

    也可用静态同步方法,但要注意访问的变量也是静态的  静态方法的锁对象是本类的class属性 - - > class文件对象(反射)

     

    (3)Lock锁

      首先在成员位置创建一个ReentrantLock对象,然后调用Lock接口的lock和unlock方法

    public class ThreadSecurity implements Runnable {
        private int ticket = 100;
        Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while (true) {
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(10);
                        System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                        ticket--;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock(); //无论程序是否异常都会释放锁
                    }
    
                }
            }
        }
    }
    Lock锁

    5.线程状态图

     生产者消费者等待唤醒案例:

    package basicpart.day01.MultiThread;
    
    public class PSmodel {
        public static void main(String[] args) {
            //1.创建一个锁对象
            Object obj = new Object();
            //2.创建消费者
            new Thread(){
                @Override
                public void run() {
                    while (true){
                        //创建同步代码块,保证一个线程等待,另一个线程执行
                        synchronized (obj){
                            System.out.println("我要一个肉包");
                            System.out.println("----------------------");
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println("那我开吃啦");
                            System.out.println("吃完啦");
                        }
                    }
                }
            }.start();
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //创建生产者
                        synchronized (obj) {
                            System.out.println("您的包子做好了");
                            System.out.println("--------------");
                            obj.notify();
                        }
                    }
                }
            }.start();
        }
    }
    View Code

     

    什么是等待唤醒机制
      这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争(race),比如去争夺锁,但这并不是故事的全部,线程间也会有协作机制。就好比在公司里你和你的同事们,你们可能存在在晋升时的竞争,但更多时候你们更多是一起合作以完成某些任务。
      就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以notifyAll()来唤醒所有的等待线程。
    wait/notify 就是线程间的一种协作机制。

    等待唤醒中的方法
    等待唤醒机制就是用于解决线程间通信的问题的,使用到的3个方法的含义如下:
    1. wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
    2. notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先入座。
    3. notifyAll:则释放所通知对象的 wait set 上的全部线程。

    调用wait和notify方法需要注意的细节

    1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。

    2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。

    3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。

    包子铺案例分析:

    代码实现:

    //资源类 包子
    public class BaoZi {
        //包子皮
        String pi;
        //包子馅
        String xian;
        //包子状态,true or false,初始值为false
        boolean flag = false;
    }
    包子类
    /*
    注意:
        1.包子铺线程和包子线程关系 - - > 通信(互斥)
        2.必须同时同步技术保证两个线程只能有一个在执行
        3.锁对象必须唯一,可以使用包子对象作为锁对象
        4.包子铺类和吃货的类需要把包子对象作为参数传递进来
            既需要在成员位置创建一个包子变量
            使用有参构造方法,为这个变量赋值
    
    */
    public class BaoZiPu extends Thread {
        private BaoZi bz;
    
        public BaoZiPu(BaoZi bz) {
            this.bz = bz;
        }
    
        //设置线程任务;生产包子
        @Override
        public void run() {
            //定义一个变量,来交替生产包子
            int count = 0;
            //让包子铺一直生产包子
            while (true) {
                synchronized (bz) {
                    //对包子的状态进行判断
                    if (bz.flag == true) {
                        //有包子,让线程等待,让吃货去吃
                        try {
                            bz.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                    //被唤醒之后执行,吃货吃完之后,包子铺生产包子
                    //增加一些趣味性:交替生产两种包子
                    if (count % 2 == 0) {
                        //生产 薄皮 韭菜馅包子
                        bz.pi = "薄皮";
                        bz.xian = "韭菜馅";
                    } else {
                        //生产 冰皮 猪肉馅
                        bz.pi = "冰皮";
                        bz.xian = "猪肉馅";
                    }
                    count++; //每次生产完一个包子加一,包子就可以交替生产不同的馅了
                    System.out.println("包子铺正在生产" + bz.pi + bz.xian + "的包子");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //包子铺生产好包子后,改变包子的状态
                    bz.flag = true;
                    //唤醒吃货线程
                    bz.notify();
                    System.out.println("新鲜的" + bz.pi + bz.xian + "包子出炉啦!!!");
                }
            }
        }
    }
    包子铺类
    public class ChiHuo extends Thread {
        private BaoZi bz;
    
        public ChiHuo(BaoZi bz) {
            this.bz = bz;
        }
    
        @Override
        public void run() {
            while (true){
                synchronized (bz){
                    if(bz.flag == false){
                        try {
                            bz.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //线程被唤醒之后,就是吃包子
                    System.out.println("吃货正在吃" + bz.pi + bz.xian + "的包子");
                    //吃货吃完包子,修改包子的状态
                    bz.flag = false;
                    //吃货唤醒包子铺生产包子
                    bz.notify();
                    System.out.println("吃货已经把" + bz.pi + bz.xian + "的包子吃完了");
                    System.out.println(" ============================= ");
                }
            }
        }
    }
    吃货类
    public class Demo {
        public static void main(String[] args) {
            BaoZi bz = new BaoZi();
            //创建包子铺线程
            new BaoZiPu(bz).start();
            //创建吃货线程
            new ChiHuo(bz).start();
        }
    }
    测试类
  • 相关阅读:
    mysql用户报错Access denied; you need (at least one of) the SUPER privilege(s) for this operation
    使用v$wait_chains分析系统为何hang住
    mongodb修改Read/Write tickets
    MongoDB 最佳实践
    Stitching模块中leaveBiggestComponent初步研究
    第四章(下)
    柱面模型解析
    2017年4月16日 一周AnswerOpenCV佳作赏析
    如何寻找已知轮廓的最大内接圆
    Stitching模块中focalsFromHomography初步研究
  • 原文地址:https://www.cnblogs.com/caixiaowu/p/12823650.html
Copyright © 2020-2023  润新知