zoukankan      html  css  js  c++  java
  • 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();
        }
    }
    测试类
  • 相关阅读:
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 3037 Saving Beans【Lucas定理】【模板题】【模板】【组合数取余】
    8.Math 对象
  • 原文地址:https://www.cnblogs.com/caixiaowu/p/12823650.html
Copyright © 2011-2022 走看看