前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java多线程--线程各状态如何进行切换

Java多线程--线程各状态如何进行切换

作者头像
张凝可
发布2019-08-22 10:56:08
1.4K0
发布2019-08-22 10:56:08
举报
文章被收录于专栏:技术圈技术圈

首先要说的是线程状态,了解了线程状态以及状态切换的过程基本上就了解了多线程。

线程的状态

1、新建状态(New):新创建了一个线程对象。 2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。 3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。 4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种: (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。 (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。 (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。 5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

这里写图片描述
这里写图片描述

线程实现的两种方法–初始状态

1.继承Thread类并重写它的run方法。之后创建这个子类的对象并调用start()方法。 2.通过定义实现Runnable接口的类进而实现run方法。这个类的对象在创建Thread的时候作为参数被传入,然后调用start()方法。 两种方法均需执行线程的start()方法为线程分配必须的系统资源、调度线程运行并执行线程的run()方法。 start()和run()的区别 start()为启动一个新的进程,也就是说这个线程为Runnable,只是说明这个线程在未来会被调度运行,但是并没有进入Running状态。 run()中放着的是线程实际要做的工作,也就是程序中的一段代码来交给这个线程来执行。 start()方法为线程分配必须的系统资源、调度线程运行并执行线程的run()方法,执行start()方法进入Runnable状态,不是Running状态。在Runnable状态到Running状态的过程中实际上是run()方法从未执行到执行的转变。

调度算法Runnable–Running

从Runnable到Running需要调度策略,这就涉及到线程的优先级。 线程的优先级及设置 线程的优先级是为了在多线程环境中便于系统对线程的调度,优先级高的线程将优先执行。 一个线程的优先级设置遵从以下原则: 线程创建时,子继承父的优先级。 线程创建后,可通过调用setPriority()方法改变优先级。 线程的优先级是1-10之间的正整数。

同步机制Running–锁池状态lock pool

也就是上边说介绍的三种阻塞中的同步阻塞。 在同步机制下,当资源被一个线程访问时,上锁,其他线程就进入了锁池,也就是进入了同步阻塞状态。 什么是锁? 在java中每个对象都有一个锁,一旦这个线程获得了这个对象的锁,这里的锁还有具体分为锁定类实例、锁定类对象两种不同的锁,针对不同的锁会限制其他线程对资源的访问,其他线程则在这个线程没有释放这个对象锁之前去访问锁定的资源了。 如何加锁: 关键字synchronized -加在方法上,同步方法。锁定类实例

代码语言:javascript
复制
    public class Demo1 {     

        public synchronized void m1(){     
            //...............     
        }     

        public void m2(){     
            //............     

            synchronized(this){     
                //.........     
            }     

            //........     
        }     
    }    

假设 Demo1 demo = new Demo1() Thread1 thread1 = new Thread1(demo) Thread2 thread2 = new Thread2(demo) Thread1和Thread2都是重写或者是实现了run()方法,而run方法中调用了demo.m1()。那么如果Thread1进入running状态,并且抢先执行了run(),那么thread1则获得了demo的对象锁,在没有释放demo的对象锁之前,t***重点内容***hread2是无法访问demo 的m1和m2方法的。 但是这是对象锁,如果是不同的对象,则对象之间的锁机制是不会互相限制的。 -加在代码块上

代码语言:javascript
复制
public class Demo2 {     
    Object a = new Object();     
    Object b = new Object();     

    public void m1(){     
        //............     

        synchronized(a){     
            //.........     
        }     

        //........     
    }     

    public void m2(){     
        //............     

        synchronized(b){     
            //.........     
        }     

        //........     
    }     
}   

码块锁定,锁定的对象是 变量 a 或 b; (注意,a 、b 都是非static 的)如果有一个 类实例对象: demo = new Demo2(),另外有两个线程: thread1,thread2,都调用了demo 对象,那么,在同一时间,如果 thread1调用了demo.m1(),则thread2在该时间内可以访问demo.m2();但不能访问 demo.m1() 的同步块, 因为a被 thread1锁定了。 -加在类变量、类方法上

代码语言:javascript
复制
    public class Demo3 {     
        static Object o = new Object();     

        public static synchronized void m1() {     
            //....     
        }     

        public static void m2() {     
            //...     
            synchronized (Demo3.class) {     
                //.....     
            }     
            //.....     
        }     

        public static void m3() {     
            //..........     
            try {     
                synchronized (Class.forName("Demo3")) {     
                  //............     
                }     
            } catch (ClassNotFoundException ex) {     
            }     
            //.............     
        }     

        public static void m4() {     
            //............     
           synchronized(o){     
             //........     
           }     
            //..........     
        }     
    }    

锁定的是类对象,因为同步的资源是属于类对象的,在这种情况下,如果thread1 访问了这4个方法中的任何一个, 在同一时间内其它的线程都不能访问 这4个方法。 但是需要注意的是: 如果在类中使用synchronized关键字来定义非静态方法,那将影响这个中的所有使用synchronized关键字定义的非静态方法。如果定义的是静态方法,那么将影响类中所有使用synchronized关键字定义的静态方法。静态方法和非静态方法的情况类似。但静态和非静态方法不会互相影响 同步机制存在的初衷 避免修改修饰、读脏数据以及保障操作的原子性。 锁释放和锁获取的内存语义。 当线程释放锁时,JVM会把该线程对应的本地内存中共享变量刷新到主内存中。 当线程获取时,JVM会把该线程对应的本地内存置为无效,从而使得被锁保护的邻界区代码必须从主内存中读取共享变量。 在线程的内存管理中,共享变量存放在堆中,但是为了保障线程之间的独立性,每个线程有一个栈内存,也就是每个线程所对应的本地内存了。 可以通过下面这段话来进一步理解线程 如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,它们对该成员变量是彼此影响的,也就是说一个线程对成员变量的改变会影响到另一个线程。   如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝(即便是同一个对象中的方法的局部变量,也会对每一个线程有一个拷贝),一个线程对该局部变量的改变不会影响到其他线程。   当一个线程进入到一个对象的synchronized方法,那么其他线程是可以进入这个对象的非synchronized方法,但是不可能进入synchronized方法。

Running–wait poll

  从Running状态到阻塞状态,主要是线程调用了wait()方法。   线程调用了wait()之后,释放掉锁,进入等待池,直到收到其他线程的通知才能从等待阻塞状态恢复到锁池状态,也就是同步阻塞状态。   或许你在想从一个阻塞状态恢复到另外一个阻塞状态有什么区别。其实如果线程在等待阻塞状态的它是没有机会恢复到Runnable的,而同步阻塞状态则可以。 wait()   wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。   当前的线程必须拥有当前对象的monitor,也即lock,就是锁。   线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。   要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。 wait()和sleep()   当线程调用了wait()方法时,它会释放掉对象的锁。   另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁。     notify()方法会唤醒一个等待当前对象的锁的线程。   如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个)。   被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁。   被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势,对象的下一个线程还是需要通过一般性的竞争。 notify()   是被拥有对象的锁的线程所调用。   (This method should only be called by a thread that is the owner of this object’s monitor.)   换句话说,和wait()方法一样,notify方法调用必须放在synchronized方法或synchronized块中。   wait()和notify()方法要求在调用时线程已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或synchronized块中.

其他阻塞状态

sleep()   线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态,在规定时间内,这个线程是不会运行的 join()   join是Thread类的一个方法,启动线程后直接调用,即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行.   内部实现代码

代码语言:javascript
复制
public final synchronized void join(long millis) 
    throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
        wait(0);
        }
    } else {
        while (isAlive()) {
        long delay = millis - now;
        if (delay <= 0) {
            break;
        }
        wait(delay);
        now = System.currentTimeMillis() - base;
        }
    }
    }

join(0)==join()意思为永远等待,只要当前线程的生命周期没有结束。 join()方法的必要性 在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。

Running-Runnable

yield() 暂停当前正在执行的线程对象,并执行其他线程。 Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。 yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。 这样就基本把java中涉及到的多线程都归纳了。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年08月19日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 线程的状态
  • 线程实现的两种方法–初始状态
  • 调度算法Runnable–Running
  • 同步机制Running–锁池状态lock pool
  • Running–wait poll
  • 其他阻塞状态
  • Running-Runnable
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档