线程概述:
进程包含三个属性:
1、独立性:每个是系统中独立存在的实体,可以拥有自己的独立资源,每一个进程都拥有自己私有的地址空间,在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。
2、动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,在进程中加入了时间的概念,进程具有自己的生命周期和各种不同的状态,这些概念在程序中是不具备的。
3、并发性:多个进程可以再单个处理器上并发执行,多个进程之间不会相互影响。
并发性和并行性的概念:
并行性指在同一时刻,有多条指令在多个处理器上同时执行;
并发性指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行。
线程是进程的执行单元,当进程被初始化后,主线程就被创建了。
一个程序运行之后至少包含一个进程,一个进程里至少包含一个线程。
多线程的优势:
进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大的提高了运行效率。
线程比进程拥有更高的性能,这是由于同一个进程中的线程都有共性,多个线程共享同一个进程的虚拟空间,线程共享的环境有:进程代码段、进程的共有数据等,利用这些共享的数据,线程很容易实现相互通信。
多线程编程的优点:
1、进程之间不能共享内存,但线程之间共享内存非常容易。
2、系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务比多进程的效率高。
3、Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了java的多线程编程
线程的创建和启动:
Java使用Thread类代表线程,所有的线程必须是Thread类或其子类的实例。
Thread.currentThread():currentThread()是Thread类的静态方法,该方法总是返回当前正在执行的线程对象。
getName():该方法时Thread类得实例方法,该方法返回调用该方法的线程名字。
使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。
使用Runnable接口的方式创建的多个线程可以共享线程类的实例属性。
使用Callable和Future创建线程:
java提供了Callable接口,该接口提供了一个call()方法作为线程执行体,call()方法具有返回值,可以声明抛出异常。
Java5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个FutureTask实现类,该类实现了Future接口并实现类Runnalbe接口,可以作为Thread类得target(目标)。
Callable接口有泛型限制,Callable接口里的泛型形参类型与call()方法返回值类型相同。
创建线程的三种方式对比:
可以将实现Runnable和实现Callable看做同一种方式。
当采用实现Runnable和实现Callable来创建多线程:
线程只是实现了Runnable、Callable接口还可以继承其它类;
在实现的情况下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰地模型,较好的体现了面向对象的思想;
当采用继承Thread类:
线程不能继承其它的父类,但需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
线程的生命周期:
当程序使用new关键字创建一个线程之后,java虚拟机为其分配内存,并初始化其成员变量的值。当线程对象调用了start()方法之后,该线程处于就绪状态,java虚拟机会为其创建方法调用栈和程序计数器,处于这个状态中的线程并没有开始运行,只是表示该线程可以运行了,至于何时运行取决于JVM里线程调度器的调度。
永远不要调用线程对象的run()方法。。。如果直接调用run()方法系统会把线程对象当成一个普通对象,而run()方法也是一个普通方法。
调用线程的run()方法之后,该线程已经不再处于新建状态,只能对处于新建状态的线程调用start()方法,否则将引发IllegalThreadStateException异常。
线程的3中结束方式,结束后相当于死亡状态:
1、run()或call()方法执行完成,线程正常结束。
2、线程抛出一个未捕获的异常或Error.
3、直接调用该线程的stop方法来结束该线程。
当主线程结束时,其它线程不受任何影响,并不会随之结束,一旦子线程启动起来后,它就拥有了和主线程相同的地位,它不会受主线程的影响。
测试某个线程是否已经死亡可以调用线程对象的isAlive方法,当线程处于就绪、运行、阻塞3中状态时,该方法返回true,当线程处于新建、死亡2中状态时,该方法返回false。
线程一旦死亡就不可以再次作为线程执行。
对新建状态的线程两次调用start()方法和调用已经死亡状态的线程都将引发IllegalThreadStateException异常。
Join线程讲解:
Thread提供了让一个线程等待另一个线程完成的方法-------join方法。当在某个程序执行流中调用其他线程的join方法时,调用线程将被阻塞,直到被join方法加入的join线程执行完为止。
Join方法的3中重载形式:
1、join():等待被join的线程执行完成。
2、join(long millis):等待被join的线程的时间最长为millis毫秒。
3、Join(long millis,int nanos):等待被Join的线程的时间最长为Millis毫秒加nanos微秒。
后台线程讲解:
后台线程就是在后台运行,为其他线程提供服务的线程。
后台线程的特征:如果所有的前台线程都死亡,后台线程会自动灭亡。
调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程。
Thread类提供了一个isDaemon()方法,用于判断指定线程是否是后台线程。
前台线程创建的子线程是前台线程,后台线程创建的子线程是后台线程。
将某个线程设置成后台线程,必须在该线程启动之前设置,也就是说,setDaemon(true)方法必须在start()方法之前调用,否则会引发IllegalThreadStateException异常。
睡眠线程讲解:
如果需要让当前线程正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类得静态sleep()方法来实现。
Sleep()方法的两种重载形式:
1、Static void sleep(long millis):让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度与准确度的影响。
2、static void sleep(long millis,int nanos): 让当前正在执行的线程暂停millis毫秒加nanos微秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度与准确度的影响。
调用sleep()方法将会抛出异常。该方法用来暂停程序的执行。
线程让步(yield)讲解:
Yield是Thread类提供的一个静态方法,它可以让当前正在执行的线程暂停,但它不会阻塞该线程,只是将该线程转入就绪状态。Yeild()方法只是让当前线程暂停一下,让系统的线程调度器重新调度一次。
Yield()方法与sleep()方法的区别:
1、Sleep()方法暂停当前线程后,会给其它线程执行机会,不会理会其它线程的优先级;但yield()方法只会给优先级相同,或优先级更高的线程执行机会。
2、sleep()方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态;而yield()不会将线程转入阻塞状态,它只是强制让当前线程进入就绪状态。
3、sleep()方法声明抛出InterruptedException异常,所以调用sleep()方法时要么捕捉该异常,要么显示声明抛出该异常,而yield()方法则没有声明抛出任何异常。
4、sleep()方法比yield()方法有更好的可移植性。
改变线程优先级讲解:
每个线程执行时都具有一定的优先级,优先级高的的线程获得较多的执行机会,而优先级低的获得较少的执行机会。
每个线程默认的优先级都与创建它的父线程的优先级相同,默认情况下,main线程具有普通优先级。
Thread类提供了setPriority(int new Priority)、getPriority()方法来设置和返回指定线程的优先级,其中setPriority()方法的参数可以是一个整数,范围是1-10之间。也可以使用Thread类的3个静态常量:
1、MAX_PRIORITY:其值是10.
2、MIN_PRIORITY:其值是1.
3、NORM_PRIORITY:其值是5
线程同步讲解:
任何时刻只能有一个线程可以获得同步监视器的锁定,当同步代码块执行完成之后,该线程会释放对该同步监视器的锁定。
Java允许程序使用任何对象作为同步监视器。
同步监视器的目的是:阻止两个线程对同一个共享资源进行并发访问。
同步方法:就是使用synchronized关键字来修饰某个方法,则该方法成为同步方法,对于同步方法而言,无须显示指定同步监视器,同步方法的同步监视器是this,也就是该对象的本身。
Synchronized关键字可以修饰方法,可以修饰代码块,但不能修饰构造器、属性等。
释放同步监视器的锁定:
1、当前线程的同步方法、同步代码块执行结束,当前线程即释放同步监视器。
2、当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行,当前线程将会释放同步监视器
3、当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致了该代码块、该方法异常结束时,当前线程将会释放同步监视器。
4、当前线程执行同步代码块过同步方法时,程序执行了同步监视器对象的wait()方法,则当前线程暂停,并释放同步监视器。
线程不会释放同步监视器的锁定的情况:
1、线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行,当前线程不会释放同步监视器。
2、线程执行同步代码块时,其它线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放同步监视器。
同步锁(Lock)讲解:
Lock是控制多个线程对共享资源进行访问的工具
ReentrantLock(可重入锁)是Lock接口的实现类。
不可变类的线程总是安全的,因为他的对象不可改变;但可变类的对象需要额外的方法来保证其线程安全,所以,Synchronized用于可变属性和方法上。