zoukankan      html  css  js  c++  java
  • 多线程(一)两种线程的实现方法、后台线程和线程组、线程的生命周期

    一、线程的两种启动方法

    分别是继承java.lang.Thread类和实现java.lang.Runnable接口两种方法。

     举个例子:

    package base;
    
    public class ThreadTest extends Thread{
        private int count=10;
        public void run()
        {
            for(int i=0;i<10;i++)
                if(count>0)
                System.out.println(this.getName()+":count"+--count);
        }
    
    public static void main(String []args)
    {
        ThreadTest t1=new ThreadTest();
        ThreadTest t2=new ThreadTest();
        ThreadTest t3=new ThreadTest();
        t1.start();
        t2.start();
        t3.start();
    
    }
    }

    结果显示3个线程各记了10个数,但是并非按照顺序来排列:

    Thread-0:count9
    Thread-2:count9
    Thread-1:count9
    Thread-2:count8
    Thread-1:count8
    Thread-2:count7
    Thread-1:count7
    Thread-2:count6
    Thread-1:count6
    Thread-1:count5
    Thread-1:count4
    Thread-1:count3
    Thread-1:count2
    Thread-1:count1
    Thread-1:count0
    Thread-2:count5
    Thread-2:count4
    Thread-2:count3
    Thread-2:count2
    Thread-2:count1
    Thread-2:count0
    Thread-0:count8
    Thread-0:count7
    Thread-0:count6
    Thread-0:count5
    Thread-0:count4
    Thread-0:count3
    Thread-0:count2
    Thread-0:count1
    Thread-0:count0

     接着是Runable接口的展示,但是与Thread类有所不同,因为接口的特性可以实现多个,而不能继承多个父类

    package base;
    
    public class RunnableTest implements Runnable{
        private int count=10;
    public void run()
    {
        for(int i=0;i<10;i++)
            if(count>0)
            System.out.println(Thread.currentThread().getName()+":count"+--count);
    
    }
    public static void main(String []args)
    {
        RunnableTest rt=new RunnableTest();
        Thread th1=new Thread(rt);
        Thread th2=new Thread(rt);
        Thread th3=new Thread(rt);
        th1.start();
        th2.start();
        th3.start();
    ;
    }
    }

    结果:

    Thread-1:count9
    Thread-2:count8
    Thread-0:count7
    Thread-2:count5
    Thread-1:count6
    Thread-2:count3
    Thread-0:count4
    Thread-2:count1
    Thread-1:count2
    Thread-0:count0

    可以看出,接口可以共用资源,3个线程一共计了10个数。

    总结一下:一个类继承Thread()类之后,实现run()方法之后。然后只需在主函数(或其他合适的地方)实例化并且调用start()方法即可。

    而使用接口的情况,需要一个类实现该接口,然后实现run()方法。在主函数中实例化该类,然后将该实例化对象作为参数创建一个新的Thread()对象,最终调用start()方法。

    其实所谓多线程就是指多个Thread()对象之间不一定按照顺序执行了!

    二、后台线程和线程组

    这两个概念似乎不经常出现,但是有必要在线程生命周期之前熟悉它们。

    后台线程daemon thread 是相对于用户线程user thread来说的。有些称为守护线程或者精灵线程,当一个程序中只有后台线程时,程序会立刻退出。如果还存在其他用户线程那么程序不会中止。而我们可以通过isDaemon()方法判断一个线程是否是后台线程。两者之间可以相互切换,通过setDaemon(boolean on)如果参数是true,该线程将会被设置为后台线程,false则会被设置为用户线程。不过该方法的调用只能在start()之前,一旦线程启动,那么它的属性就无法再进行切换。

    接下来是后台线程的一个简单是示例:

    package base;
    
    public class DaemonThread extends Thread{
    public void run()
    {
        for(int i=0;i<10;i++)
        {
            System.out.println("线程"+i);
            try 
            {
                sleep(3000);
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    public static void main(String []args)
    {
        Thread a=new DaemonThread();
        a.setDaemon(true);
        a.start();
        if(a.isDaemon())
        {
            System.out.println("IsDaemon");
        }
        else
        {
            System.out.println("IsNotDaemon");
        }
        System.out.println("XXX");
        
    }
    }

    结果:

    IsDaemon
    XXX
    线程0

    结果代表当主线程运行完毕后,即“XXX”打印完毕后,就只剩下后台线程了,仍然再暂停中,因此程序直接结束,至于打印出线程0是因为那是在程序结束之前已经运行完毕。

    可以想象的,当我们把setDaemon()参数设置为false或者删掉这句代码,那么程序将会在上面结果的基础上每隔3000ms打印出一个线程i,直到结束。而如果把sleep代码去掉但是保留setDaemon的部分,那么结果也会打印出所有的线程的结果。猜想是后台线程运行较快,在主线程运行结束前就已经结束,只有使用挂起才能观察的效果,这也是为什么示例都会采取挂起的动作。

    于是我就想到把10次循环改为无限循环,看在主线城结束时能打印多少?

    经过实测,打印到了线程17,基本大致上验证了咱们之前的猜想(虽然每次结果并不相同)。

    线程组ThreadGroup好像比较相对的不重要,不过倒是学到了一些方法的使用:

        {
            Thread t=Thread.currentThread();
            System.out.println(t.getName());
            ThreadGroup tg=t.getThreadGroup();
            System.out.println(tg.getName());
            System.out.println("当前线程组含有"+tg.activeCount()+"个线程");
            int n=tg.activeCount();
            Thread [] th=new Thread [n];
            tg.enumerate(th);
            for (Thread a:th)
                System.out.println(a.getName());
    
            tg=tg.getParent();
            System.out.println(tg.getName());
            System.out.println("当前线程组含有"+tg.activeCount()+"个线程");
    
            int m=tg.activeCount();
            Thread [] thr=new Thread [m];
            tg.enumerate(thr);
            for (Thread b:thr)
                System.out.println(b.getName());
    
    
        }

    例如currentThread()获得当前线程,是静态方法,Thread类调用。getThreadGroup()获得当前线程组,不是静态方法,Thread对象调用。activeCount()表示线程组中的线程数量。还有enumerate(a)将线程组里的线程赋给线程数组a。getName()获得线程或者线程组的名称。

    对了,贴上结果:

    三、线程的生命周期和Java中的线程方法

    当我们new一个新的Thread的对象时,线程就进入到了新生态,而调用start()方法就进入了就绪态(或可执行状态),JVM通过一定的调度规则使就绪态的线程变成运行态,运行态执行run()方法,执行结束后转变为死亡态。运行态的线程调用静态方法sleep(long millis)进入睡眠态,过了指定的睡眠时间(millis/1000)秒,线程重新变为运行态,接着运行。调用成员方法wait()方法进入等待态,需要别的线程调用Object的成员方法notify()或者notifyAll(),又或者时间到了,有可能(?)唤醒重新进入就绪态,重新等待JVM的调度。

    介绍阻塞态,需要先介绍一个新的概念——线程的优先级。每个线程都有优先级,在Java中,线程的最小优先级为常量Thread.MIN_PRIORITY,其值为1。最大的优先级为Thread.MAX_PRIORITY,值为10。如果不设置优先级,那么线程的默认优先级为Thread.NORM_PRIORITY,值为5。通过getPriority()方法可以得到线程的优先级,而setPriority(int newPriority)则是设置优先级,当设置的参数大于所允许的最大优先级时,线程的优先级就变为所允许的最大优先级。

    为什么会有最大优先级呢?因为这里有一个设置最大优先级的方法:setMaxPriority(int maxP)。同样的,也能查到最大优先级getMaxPriority()。但是线程的最大优先级不能超过父线程组的最大优先级,因此会在maxP和父线程最大优先级选择较小的那一个。注:maxP不能超过正常的优先级范围,否则不起作用。

    当有多个处于就绪态线程时,优先级高的线程会优先执行,进入运行态。优先级一样则随机选择。如果线程共享资源,但是资源只能被有限个线程占用,JVM就会优先选取优先级高的线程进入运行态,而其余的就绪态线程由于资源短缺就会转换为阻塞态。当资源准备就绪,则阻塞态线程会自动重新进入就绪态。

    小笔记:

    mythread.start()会启动一个新线程,并在新线程中运行run()方法。
    而mythread.run()则会直接在当前线程中运行run()方法,并不会启动一个新线程来运行run()。

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 数字黑洞
    Minifilter微过滤框架:框架介绍以及驱动层和应用层的通讯
  • 原文地址:https://www.cnblogs.com/lbrs/p/10591109.html
Copyright © 2011-2022 走看看