当前位置 博文首页 > 文章内容

    Java多线程全知识(除线程池外)

    作者: 栏目:未分类 时间:2020-09-23 15:59:57

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    多线程

    进程与线程

    • 一个进程有多个线程

    • 进程之间相互隔离,线程之间可以相互通信

    • cpu一个时间点只能执行一个线程,但多个线程之间的切换比较快,给人一种多个线程同时执行的错觉

    实现多线程的3种方法

    1. 继承Thread类(不推荐)

      • 创建类,继承Thread类

      • 重写run()方法

      • 创建对象,使用start()方法启动线程

      Java有单继承的局限性,尽量不使用这种方法

    2. 实现Runnable接口(推荐使用)

      • 创建类,实现Runnable接口

      • 重写run()方法

      • 创建对象,并将其作为Thread类的构造参数,创建Thread类,调用start()方法启动线程

      这种方法避免使用继承,缓解了Java单继承的局限性,并且使用静态代理,可以节约资源

      使用到了静态代理,一个资源(对象),多次代理(Thread类代理、多个线程使用一个对象)

    3. 实现Callable接口(投入工作后会使用到,前期不使用)

    线程的状态以及操作

    状态

    线程状态图

    • 准备状态:当new一个线程(new Thread())时,进入准备状态

    • 就绪状态

      • 事件1:线程调用start()方法,启动线程,进入就绪状态,开始和其他线程抢占cpu资源

    • 运行状态

      • 事件2:通过调度算法,线程被cpu调度,获取到cpu资源,进程线程内的逻辑操作,进入运行状态

    • 阻塞状态

      • 事件3:线程调用sleep、wait方法或者同步锁定时,当前线程进入阻塞状态

      • 事件4:阻塞时间结束后,则重新进入准备就绪状态,开始抢占cpu资源

    • 终止(死亡)状态

      • 事件5:线程被停止、线程逻辑操作执行完毕等事件发生后,线程进入终止状态

      • 一旦进入死亡状态的线程将不能被再次启动

    操作

    • 线程停止

      • 不建议使用stop()、destory()等方法,容易造成为止的错误

      • 建议设置一个Boolean值进行控制,要停止线程时,就将其切换为false

    • 线程礼让

      • yield(),线程调用该方法后进入就绪状态,重新和其他线程抢占cpu

    • 线程休眠

      • sleep(),线程调用该方法后进入阻塞状态,过了设定的时间后,进入继续状态,和其他线程抢占cpu

    • 线程强制执行

      • join(),线程调用该方法后立即进入运行状态,之前正在运行的线程进入阻塞状态

    线程状态观测

    • 使用Thread.getState()来获取该线程的状态

    • 线程状态有几个常量

      • NEW:尚未启动的线程处于此状态[刚new出来的线程]

      • RUNNABLE:在Java虚拟机中执行的线程处于此状态[调用start()方法后的线程]

      • BLOCKED:被阻塞等待监视器锁定的线程处于此状态

      • WAITING:正在等待另一个线程执行特定动作的线程处于此状态

      • TIME_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态[调用了sleep的线程]

      • TERMINATED:已退出的线程处于此状态

    线程优先级

    • getPriority、setPriority分别用来获得和设置线程的优先级

    • 线程优先级的范围在1-10,MIN_PRIORITY=1, MAX_PRIORITY=10, (默认优先级)NORM_PRIORITY=5

    • 设置优先级要在线程start之前设置,不然无效

    • 优先级高的不一定先执行,只是被执行的概率更高,同时这会引发一个性能倒置的问题

    守护线程

    • Java虚拟机只需要保证执行完用户线程(非守护线程)

    • setDaemon(true),即可使该线程成为守护线程,默认为false

    线程同步

    多个线程操作同一个资源,会导致一些不安全的事件发生。例如,银行取钱,两个人同时取一个银行卡的钱,两人取钱的总额大于银行卡余额,结果余额就会出现负数。这就是一种严重的错误。

    所以我们要用到队列

    当一个线程操作一个资源的时候,给他一个锁,这样来防止同一时间有其他线程来操作这个资源。在使用过资源后,释放锁,交由下一个线程。也就形成了一个队列。

    加锁

    1. 使用synchronized关键字

      每个对象都有一把锁

      1. 对方法加锁

        public synchronized void method(){

        逻辑代码

        }

        这样就实现对方法加锁,在线程调用这个方法的时候,就会默认使用this对象(线程对象)来加锁

      2. 代码块加锁

        @Override

        public void run(){

        synchronized (一个对象,一般使用被操作的资源){

        逻辑代码

        }

        }

        当运行到代码块时,括号里的对象将会被锁上,逻辑代码执行完之前,其他线程不能访问

    2. 使用Lock(接口)锁

      ReentrantLock类实现了Lock接口

      1. 使用方法

        class testLock implements Runnable{

        private final ReentrantLock lock =new ReentrantLock();

        @Override

        public void run() {

        while(true){

        try {

        lock.lock();//加锁

        逻辑代码

        }finally {

        lock.unlock();//解锁

        }

        }

      synchronized与Lock的区别,前者是隐性的上锁开锁,在进入被其修饰的代码时自动上锁,在跳出代码块后自动解锁

      而Lock则是需要手动的上锁解锁,是显性的

    死锁

    两个线程在分别已经拥有一个对象锁并且未解锁的情况下,互相请求获取对方的对象锁,就会造成死锁

    例如:小明拿着玩具1,小红拿着玩具2,两人想要对方的玩具的同时,还不想放弃自己的玩具。两人就会僵持住(死锁)

    生产者和消费者

    wait()方法:this.wait,会释放锁,所以一般都是使用的同一个对象

    notifyAll():通知所有wait的线程启动

    线程池

    放在其他地方