java多线程(一)多线程基本技能

1、进程和多线程的概念及线程的优点:

        进程:进程是操作系统结构的基础,是一个次程序的执行,是一个程序及其数据的处理机上所发生的活动,是程序在一个数 据集合上运行的过程,它是系统进行资源分配的独立单位。进程可以理解为Windows管理器中显示的运行的exe程序。进程是受操作系统管理的基本运行单元。
        线程:可以理解成是是在进程中独立运行的子任务。

2、使用多线程:

        一个进程在运行时至少会有1个线程在运行。 

2.1、继承Thread类:

            Thread类的结构:
                public class Thread implements Runnable
                从上面的源代码可以发现,Thread类实现了Runnable接口,他们之间具有多台的关系。
            其实,使用继承Thread类的方式创建新线程时,最大的的局限就是不支持多继承,因为java语言的特点就是单一继承,
            所以为了支持多继承,完全可以实现Runnable接口的方式,一边实现一边继承。但是这两种方式创建的线程在工作时的
            性质是一样的,没有本质的区别。
            步骤:
             1、定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程要完成的任务。因此把run()方法称为线程执行体。
             2、创建Thread子类的实例,即创建了线程对象。
             3、调用线程对象的start()方法来启动多线程。   
            //代码的运行结果与代码执行顺序或调用顺序是无关的。线程具有随机性。
            //另外还需要注意,执行start()方法的顺序不代表线程启动的顺序。

2.1、实现Runnable接口:

            如果欲创建的线程已经有一个父类了,这时就不能继承自Thread类了,因为java不支持多继承,
            所以就需要实现Runnable接口来应对这样的情况。
            步骤:
                1、定义Runnable接口的实现类,并重写该接口的run()方法,该run方法的方法体同样是该线程的线程执行体。
                2、创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
            Thread.java类也实现了Runnable接口,这就意味着构造函数Thread(Runnable target)不光可以传入Runnable接口的对象,还可以传入一个Thread类的对象。   

2.3、实例变量与线程安全:

            自定义线程类中的实例变量针对其他线程可以有共享与不共享之分。
            1、不共享数据的情况:
                每个线程都有各自的count变量,自己减少自己的count变量的值。
                这样的情况就是变量不共享。此示例并不存在多个线程访问同一个示例变量的情况。
            2、共享数据的情况:
         共享数据的情况就是多个线程可以访问同一个变量,比如在投票功能的软件时,多个线程可以同时处理一个人的票数。    
       通过在run()方法前加入synchronized关键字,使多个线程在执行run()方法时,以排队的方式进行处理。当一个线程调用run前,先判断run方法有没有被上锁,如果上锁,说明有其他线程正在调用run方法, 必须等其他线程对run方法调用结束后才可以执行run方法。这样也就实现了排队调用run方法的目的。
         synchronized可以在任意对象及方法上加锁,而加锁的这段代码称为"互斥区" 或 "临界区"。 当一个线程想要执行同步方法里面的代码时,线程首先尝试去拿这把锁,如果拿到能够拿到这把锁,那么这个线程就可以执行synchronized里面的代码。如果不能拿到这把锁,那么这个线程就会不断尝试拿这把锁,知道能够拿到为止,而且是有多个线程同时去争抢这把锁。
       非线程安全:
                    主要是指多个线程对同一对象中的同一实例变量进行操作时会出现值被更改、值不同步的情况,
                    进入影响程序的执行流程。
                    解决非线程安全问题使用的是synchronized关键字。

3、currentThread()方法

        currentThread()方法可返回代码正在被哪个线程调用的信息。   

4、isAlive()方法:

        方法isAlive()的功能是判断当前的线程是否处于活动状态。
        什么是活动状态?
            活动状态就是线程启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程时"存活"的。

5、sleep()方法:

        方法sleep()的作用是在指定的毫秒数内让当前"正在执行的线程"休眠(暂停执行)。
        这个"正在执行的线程"是指this.currentThread()返回的线程。    

6、getId()方法:

        getId()方法的作用是取得线程的唯一标识。

7、停止线程:

        停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。
        //停止一个线程可以使用Thread.stop()方法,但最好不用它,因为这个方法是不安全的。而且已被弃用作废的。 
        //大多数停止一个线程的的操作使用Thread.interrupt()方法。

       尽管这个方法的名称是"停止,终止"的意思,但这个方法不会终止一个正在运行的线程。还需要加入一个判断才可以完成线程的停止。

7.1、停止不了的线程:

            使用interrupt()方法来停止线程,但interrupt()方法的使用效果并不想for+break语句那样,马上就停止循环。
            调用interrupt()方法仅仅是在当前线程上打了一个停止的标记,并不是真正的停止线程。   

7.2、判断线程是否是停止状态:

            Thread.java类里提供了两种方法:
            1、this.interrupted(): 测试当前线程是否已经是中断状态。执行后具有将状态标志清除为false的功能。
                public static boolean interrupted()
        测试当前线程是否已经中断。线程的中断状态由该方法清除。也就是说,连续两次调用该方法,则第二次调用将返回false。
                //interrupted()方法具有清除状态的功能,所以第二次调用interrupted()方法返回的值是false。       
            2、 this.isInterrupted(): 测试线程Thread是否已经中断。但不清除状态标志。
                public boolean isInterrupted()

7.3、能停止的线程---异常法

            可以在线程中用for语句来判断一下线程是否是停止状态。如果是停止状态,则加上break后面的代码不在运行即可。

7.4、在沉睡中停止

            如果sleep状态下停止某一个线程,会进入catch语句,并且清除停止状态值,使之变成false。

7.5、能停止的线程---暴力停止

            使用stop()方法停止线程则是非常暴力的。

7.6、方法stop()与java.lang.ThreadDeath异常

            调用stop()方法时会抛出java.lang.ThreadDeath异常,但在通常的情况下,此异常不需要显示地捕捉。

7.7、释放锁的不良后果:

            使用stop()释放锁将会给数据造成不一致性的结果。如果出现这样的情况,程序处理的数据就有可能遭到破坏,
            最终导致程序执行的流程错误。

 7.8、使用return停止线程:

            将方法interrupt()域return结合使用也能实现停止线程的效果。
            不过还是建议使用"抛异常"的方法来实现线程的停止,因为在catct块中还可以将异常向上抛,使线程
            停止的事件得以传播。
            

8、暂停线程:

        暂停线程意味着此线程还可以恢复运行。在java多线程中可以使用suspend()方法暂停线程,使用resume()方法恢复线程的执行。
        1、suspend与resume方法的使用:
            suspend(): 暂停
            resume(): 恢复  
 
        2、suspend与resume方法的缺点---独占
            suspend()方法是过期作废的方法。        
        3、 suspend与resume方法的缺点---不同步    

9、yield(放弃)方法:

        yield()方法的作用是放弃当前的CPU资源,将他让给其他的任务去暂用CPU的执行时间内。但放弃的时间不确定,
        有可能刚刚放弃,马上又获得CPU时间片。 

10、线程的优先级:

        在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,
        也就是CPU优先执行优先级较高的线程对象中的任务。
        使用setPriority()方法可以设置线程的优先级。   

11、守护线程:

        在java线程中有两种线程,一种是用户线程,另一种是守护线程。
        守护线程时一种特殊的线程,当进程中不存在非守护线程了,则守护线程自动销毁。
        典型的守护线程就是垃圾回收线程。
        Daemon(守护线程,后台程序)的作用是为其他线程的运行提供便利服务。
            thread.setDaemon(true);

12、整理的思维导图:

猜你喜欢

转载自blog.csdn.net/MyronCham/article/details/82460206