2021-03-14

多线程知识点总结

一,实现Runnable与继承Thread相比的优势:

1,通过创建任务,然后给线程分配任务来实现多线程,更适合多个线程同时执行相同的任务

2,可以避免单继承所带来的局限性

3,任务与线程是相分离的,提高线程的健壮性

4,后期学习线程池,只接受Runnable类型的任务,不接受Thread类型

 

二,如何设置和获取线程名称

Thread t = new Thread(new MyRunnable())
t.setName();//设置名称
t.currentThread().getName();//获取名称

三,线程休眠sleep

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
                 for(int i=0;i<5;i++){
                           System.out.println("锄禾日当午")
                           Thread.sleep(1000);//1000相当于1秒
                    }
           }
}

四,线程中断与守护线程

例如:t1,interrupt();        //给线程t1添加中断标记

一个线程是一个独立的执行路径,它是否结束,应该由自身决定

Thread t1 = new Thread(new MyRunnable());
        //设置为守护线程
        t1.setDaemon(true);
        t1.start();
        for (int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //给线程添加中断标记
        t1.interrupt();

    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //1,不想让程序中断
                    //System.out.println("检测到标记,但我偏不死亡");
                    //e.printStackTrace();
                    //2,让程序中断
                    //System.out.println("检测到标记,我选择死亡");
                    //return;
                    e.printStackTrace();
                    return;
                }
            }
        }

线程分为守护线程和用户线程

用户线程:当一个进程中不包含任何一个存活的用户线程,线程结束

守护线程:守护用户线程,当最后一个用户线程结束时,守护线程自动死亡

t1.setDaemon(true)     //将t1设置为守护线程

五,线程安全和线程不安全

1)线程不安全:例如卖票

public static void main(String[] args) {
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();


    }
    static class Ticket implements Runnable{
        //票数
        private int count = 10;

        @Override
        public void run() {
        while(count>0){
            System.out.println("正在准备卖票");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count--;
            System.out.println("准备出票,剩余的票数"+count);
        }



        }
    }

假设还剩一张票,三个线程abc,进入while循环判断>0,a先经过代码块,进入count--操作,这时bc抢到时间篇,也进入count--操作,这时便会出现线程不安全情况,出现负数票

如何解决线程不安全

1,同步代码块synchronized(锁对象){}

public static void main(String[] args) {
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();


    }
    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        private Object o = new Object();

        @Override
        public void run() {
            while(true){
                synchronized (o){
                    if(count>0){
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"出票成功,剩余的票数"+count);
                    }else{
                        break;
                    }
                }
            }
        }

其中一个线程进行while..代码块时,其他线程不能插足,创建锁对象o,一个线程抢占到锁,只有当他执行完之后,其他线程才能开始抢

 

2,线程安全2,同步方法

public static void main(String[] args) {
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();


    }
    static class Ticket implements Runnable{
        //票数
        private int count = 10;

        @Override
        public void run() {
            while(true){
                boolean flag = sale();
                   if (!flag){
                       break;
                   }
            }
    }

    public synchronized boolean sale() {
            if (count>0) {

                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"准备出票,剩余的票数" + count);
                return true;
            }return false;
        }
     }

创建boolean类型的sale方法,用synchronized修饰sale方法,没进入if语句,则证明没票了

flag为true买票成功,如果为false,则证明买票失败,break结束

 

3,线程安全3,显示锁

public static void main(String[] args) {
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();


    }
    static class Ticket implements Runnable{
        //票数
        private int count = 10;
        //显示锁
        //private Lock l = new ReentrantLock();
        //显示锁l:fair参数为true,就表示公平锁
        private Lock l = new ReentrantLock(true);

        @Override
        public void run() {
            while(count>0){
                l.lock();
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"准备出票,剩余的票数"+count);
            }
            l.unlock();

同步代码块和同步方法都为隐式锁

创建显示锁Lock l = new ReentrantLock();

在进入if语句前,进行上锁   l.lock(),  执行完毕解锁  l.unlock()

 

公平锁与非公平锁:

以上三种线程安全都为非公平锁,大家一起抢占,公平锁则为先到先得,排队执行

公平锁:只需要在创显示锁时传入true

Lock l = new ReentrantLock(true)

 

 

六,线程死锁

举例解释线程死锁:ab去两个试衣间,a进1试衣间,将其上锁,b进入2试衣间进行上锁,这时a觉得对自己的1试衣间不满意想换到2,而b也对自己的2试衣间不满意,想换到1试衣间,ab都在等对方先出来,才能进入对方的试衣间,这时便出现线程死锁

 

public static void main(String[] args) {
        Culprit c = new Culprit();
        Police p = new Police();
        new MyThread(c,p).start();
        c.say(p);


    }

    static class MyThread extends Thread{
        private Culprit c;
        private Police p;
        public MyThread(Culprit c,Police p){
            this.c = c;
            this.p = p;
        }
        public void run(){
            p.say(c);
        }
    }
    //罪犯
    static class Culprit{
        public synchronized void say(Police p){
            System.out.println("罪犯:你放我了,我就放了人质");
            p.fun();
        }
        public synchronized void fun(){
            System.out.println("罪犯被释放了,人质也获救了");
        }

    }
    //警察
    static class Police{
        public synchronized void say(Culprit c){
            System.out.println("警察:你放了人质,我就放了你");
            c.fun();
        }
        public synchronized void fun(){
            System.out.println("警察救了人质,但是罪犯跑了");
        }

    }

用代码来解释死锁,只有当罪犯放了人质,警察才能放了罪犯,而罪犯只有当警察放了他,他才能放了人质,于是双方一直僵持着

 

七,多线程通信问题   生产者与消费者

只有当生产者生产出东西,消费者才能使用

public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();


    }
     //厨师
    static class Cook extends Thread{
        private Food f;

         public Cook(Food f) {
             this.f = f;
         }

         public void run(){
             for (int i=0;i<100;i++){
                 if(i%2 == 0){
                     f.setNameAndTaste("老干妈小米粥","酸辣味");
                     }else{
                     f.setNameAndTaste("煎饼果子","甜辣味");
                 }
             }
         }
     }
     //服务生
    static class Waiter extends Thread{
         private Food f;

         public Waiter(Food f) {
             this.f = f;
         }
         public void run(){
             for (int i=0;i<100;i++){
                 try {
                     Thread.sleep(100);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 f.get();
             }
         }
    }
     //食物
    static class Food{
        private String name;
        private String taste;
        //true表示可以生产
        boolean flag = true;

        public synchronized void setNameAndTaste(String name,String taste){
            if (flag){
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
            }
            flag = false;
            this.notifyAll();
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public synchronized void get()  {
            if (!flag){
                System.out.println("服务生端走的菜的名称:"+name+",菜的味道:"+taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

厨师和服务员:没加休眠和唤醒时,可能会出现的问题:将菜的名字和味道整混,例如:煎饼果子本是甜辣味,而最终显示的却是酸辣味,如果用synchronized修饰时会出现另一种情况厨师刚做完一道菜,回头又抢到时间篇,接着又做菜

或者服务员端完盘子,又抢到时间篇,接着执行端盘子操作,但是厨师并没有做菜

出现这种情况的原因是:setandtaste方法中,厨师刚执行完this.name = name;时间篇被服务员抢走了,没执行到this.taste = taste,菜就被端走了,

如何解决这种问题:厨师做菜的时候,对服务员进行休眠wait(),厨师做完菜时,唤醒服务员this.notifyAll(),然后将厨师进行休眠

 

 

八,线程的六种状态

1,new创建  2,Runnable运行  3,Blocked上锁排队  4,wait休眠  5,Tinedwainting排队休眠  6,terminated结束

 

 

九,带返回值的线程Callable

public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> c = new MyCallable();
        FutureTask<Integer> task = new FutureTask<>(c);
        new Thread(task).start();
        //判断线程任务是否执行完毕
        //task.isDone();
        //取消
        //task.cancel(true);


        Integer j = task.get();
        System.out.println("获取的返回值为:"+j);
        for (int i=0;i<10;i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
        }

    }
    static class MyCallable implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            Thread.sleep(3000);
            for (int i=0;i<10;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
            return 100;
        }
    }

主副线程交替执行,通过task.get()可以获取返回值100,task.isDone()判断线程任务是否完毕,task.cancel(true)取消

 

十,线程池

线程池的创建:

ExecutorService service = Executors.newCachThread();

1)缓存线程池

 

 /**
     * 1,缓存线程池(长度没限制)
     *  任务加入后的执行流程:
     *   1,判断缓存池是否存在空闲线程
     *   2,存在则使用
     *   3,不存在,则创建线程,并放入缓存池,然后使用
     */
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        service.execute(new Runnable() {
            @Override//已经创建三个线程,第四个任务会使用上面三个其中一个空闲的线程
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });

    }

2)定长线程池

/**
     * 定长线程池:
     *         1,判断线程池是否存在空闲空间
     *         2,存在则使用
     *         3,不存在空闲线程,且线程池未满的情况下,创建线程并放入线程中,然后使用
     *         4,不存在空闲程序,且线程池已满的情况下,则等待线程池存在空闲线程
     *
     *
     *         (单线程池,相当于把定长线程池传入的数字为1)
     */
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
    }
}

3)周期任务,定长线程池

 /**
     * 周期任务执行时:
     *       定时执行,当某个时机触发时,自动执行某任务
     *
     */
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        /**
         * 1,定时执行一次
         *     参数1:定时执行的任务
         *     参数2:时长数字
         *     参数3:时长数字的时间单位,TimeUnit的常量指定

        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("锄禾日当午");
            }
        },5, TimeUnit.SECONDS);*/


        /**
         * 周期性执行
         *     参数1,任务
         *     参数2,延迟时长数字
         *     参数3,周期时长数字
         *     参数4,时长数字的单位
         */

        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("锄禾日当午");
            }
        },5,1,TimeUnit.SECONDS);


    }
}

 

十一,lambda表达式

public static void main(String[] args) {
        print((int x, int y) ->{
                return x+y;

        },100,200);


    }
    public static void print(MyMath m,int x,int y){
        int num = m.sum(x,y);
        System.out.println(num);
    }

    static interface MyMath{
        int sum(int x,int y);

    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
embedcpp-2021-03是一个有关嵌入式C++编程的课程,于2021年3月举办。嵌入式C++编程是指在嵌入式系统中使用C++编程语言进行开发的一种方法。 在嵌入式系统中,资源通常是有限的,例如处理器速度、内存容量和存储空间等。因此,使用C++编程语言可以提供更高的灵活性和效率,帮助开发人员充分利用有限的资源。C++在嵌入式系统中的应用范围广泛,例如物联网设备、汽车电子和工业自动化等领域。 embedcpp-2021-03课程旨在向学员介绍嵌入式C++编程的基础知识和技巧。课程内容通常包括以下方面: 1. C++语法和特性:介绍C++的基本语法、面向对象编程和泛型编程等概念,以及C++11、C++14和C++17的一些新特性。 2. 嵌入式系统概述:了解嵌入式系统的基本特点、硬件和软件组成,以及与传统桌面开发的区别。 3. 低级编程:学习如何与硬件交互,包括使用寄存器、配置外设和处理中断等。还可以介绍使用汇编语言优化性能的技巧。 4. 内存管理:探讨嵌入式系统中的内存管理技术,包括堆栈和堆的使用、动态内存分配和对象生命周期管理等。 5. 实时操作系统(RTOS):介绍嵌入式系统中常用的实时操作系统,如FreeRTOS和µC/OS等,学习如何使用RTOS进行任务调度和资源管理。 除了理论知识,embedcpp-2021-03课程通常还包括实际的项目练习,以帮助学员将所学知识应用于实际场景。通过该课程,学员可以了解嵌入式C++编程的基础概念和实践技巧,为嵌入式系统开发提供了一定的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值