线程概述
当一个程序运行时,内部可能包含多个顺序执行流,每个顺序执行流就是一个线程。
进程三大特性:独立性、动态性、并发性
独立性:进程是系统中独立存在的实体,它可以拥有自己的资源,每一个进程都拥有自己独有的地址空间。
在没有经过进程本身允许的情况下,一个进程用户不可以直接访问其他进程的地址空间。
动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。
在进程中加入了时间的概念,进程有自己的生命周期和各种不同的状态。
并发性:多个进程可以在单个处理器上并发执行。
总结:操作系统可以同时执行多个任务,每个任务就是进程;
进程可以同时执行多个任务,每个任务就是线程。
多线程便层的优点:
1.进程之间不能共享内存,线程之间非常容易共享内存;
2.系统创建进程需要为该进程重新分配系统资源,但创建线程代价小的多,因此使用多线程来实现多任务并发比多进程效率高。
3.Java语言内置了多线程功能支持,而不是单纯的作为底层操作系统的调度方式,从而简化了Java的多线程编程。
创建线程的方式:
1.继承Thread类
2.实现Runnable接口
3.使用Callable和Future创建线程
提供了一个call()方法作为线程执行体,但是call方法比run()方法强大。
1.call()方法有返回值
2.可以声明拋出异常
区别:第一种方法直接创建Thread的子类即可代表线程对象,
后者创建的Runnable对象只能作为线程对象的target
线程的生命周期
新建、就绪、运行、阻塞、死亡
新建: new Thread()
就绪: new Thread().start() //(表示线程可以开始运行了,具体合适运行取决于JVM里线程调度器的调用)
运行: run()
阻塞:
1.调用sleep()方法主动放弃所占用的处理器资源
2.线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
3.线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有
4.线程在等待某个通知(notify)
5.程序调用了线程的suspend()方法将该线程挂起。这个方法容易导致死锁,尽量避免使用。
当前正在执行的线程被阻塞之后,其他线程可以获得执行的机会。被阻塞的线程会在合适的时候重新进入就绪状态。
注意是就绪状态而不是运行状态。也就是说,被阻塞线程的阻塞解除后,必须重新等待线程调度器再次调度它。
解除阻塞:
1.调用slee()方法的线程经过了指定时间
2.线程调用的阻塞式IO方法已经返回
3.线程成功获得了视图取得的同步监视器
4.线程正在等待某个通知时,其他线程发出了一个通知
5.处于挂起状态的线程被调用了resume()恢复方法
死亡:
1.run()或call()方法执行完成,线程正常结束
2.线程抛出一个未不火的Exception或Error
3.直接调用该线程的stop()方法来结束该线程————容易导致死锁,不推荐
注意:调用线程使用start()方法,而不是run()方法。永远不要调用线程对象的run()方法!
调用start()方法来启动线程,系统会把run()方法当成线程执行体来处理;
但是如果直接调用线程的run()方法,则run()方法立即就会被执行,而且在run()方法返回之前
其他线程无法并发执行————也就是说,如果直接调用线程对象的run()方法,系统会把线程对象当成一个普通对象,
而run()方法也是一个普通方法,而不是线程执行体。
注意:只能对处于新建状态的线程调用start()方法,否则将引发IllegelThreadStateException异常。
注意:如果希望调用子线程的start()方法后子线程立即开始执行,程序可以使用Thread.sleep(1)来
让当前运行的线程(主线程)睡眠1毫秒————1毫秒就够了。因为CPU会在这1毫秒内去执行另一个
处于就绪状态的线程,这样就可以让线程立即执行。
注意:当一个线程开始运行后,不可能一直处于运行状态(除非线程瞬间就执行结束了),线程在运行过程中需要被中断,
目的是使其他线程获得执行的机会。对于采用抢占式策略的系统而言,系统会给每个执行的程序一个小时间段
来处理任务;当该时间段用完后,系统就会剥夺该线程所占用的资源,让其他线程获得执行的机会。
在选择下一个线程时,系统会考虑线程的优先级。
控制线程
1.join线程————让一个线程等待另一个线程完成
eg:三个线程,主线程与线程A并发执行,当自增长的变量i=20时,线程B也开始启动。
线程B中有JoinThread.join();则主线程要等线程B执行完才可以继续执行
join()方法有三种重载方式:
join():等待被join的线程执行完成
join(long millis):等待被join的线程的时间最长为millis毫秒。
如果在millis毫秒内被join的线程还没有执行结束,则不再等待
join(long millis,int nanos):(少用)等待最长时间为millis加nanos
2.后台线程
在后台运行的,为其他线程提供服务,又称为“守护线程”或“精灵线程”。eg:JVM的垃圾回收线程
特征:如果所有的前台线程都死亡,后台线程会自动运行
设置为后台进程:Thread.setDaemon(true)
当所有前台线程死亡时,后台线程随之死亡
3.线程睡眠————sleep()是静态方法
static void sleep(long millis):暂停当前执行线程mills毫秒,进入阻塞状态
static void sleep(long millis,int nanos):用的很少
4.线程让步:yield
yield():让正在执行的线程暂停,但不会阻塞线程。
将现场转入就绪状态,等待系统线程调度器重新调度。
注意:当线程调用yield()暂停后,只有优先级与当前线程相同或高于当前线程,才有获得执行的机会
sleep()与yield()方法区别:
1.sleep()暂停当前线程后,会给其他线程执行机会,不理会其他线程的优先级;
yield()方法只给优先级相同或更高级别的线程执行。
2.sleep() 会将策划程序转入阻塞状态,yield()不会
3.sleep() 方法声明跑出来InterruptedException异常,调用该方法时需要捕捉或抛出异常;
yield() 方法,没有声明抛出任何异常
4.sleep()方法比yield()方法有更好的移植性,通常不建议使用yield()方法来控制并发程序的执行
5.改变线程优先级
每个线程默认的优先级都与创建它的父线程的优先级相同,默认情况下,main线程具有普通优先级。
设置优先级:setPriority(int newPriority),newPriority的值在0~10之间
Thread类有3个静态常量: MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
6.传统的线程通信
为实现线程通信,可借助Object类的wait()、notify()、notifyAll()这三个方法,他们不属于Thread类。
这三个方法必须由同步监视器对象来调用,有以下两种情况:
1.对应synchronized修饰的同步方法,该类的默认实例(this)就是同步监视器,
可以在同步方法中直接调用这三个方法。
2.对于使用synchronized修饰的同步代码块,同步监视器是synchronized括号里面的对象,
所以必须使用该对象调用这三个方法。
wait()、notify()、notifyAll()三方法解释:
wait():导致当前线程等待,直到其他线程调用该同步监视器的notify()方法或notifyAll()方法来唤醒该线程。
wait()方法有三种形式————无时间参数的wait(一直等待,知道其他线程通知)、带有毫秒参数的wait()和带毫秒、毫微秒参数的wait()
(这两种方法都是等待指定时间后自动苏醒)。调用wait()方法的当前线程会释放对该同步监视器的锁定。
notify():唤醒在此同步监视器上等待的单个线程。如果所有线程都在此同步监视器上等待,则会选择唤醒其中一个线程。选择是任意的。
notifyAll():唤醒在此同步监视器上等待的所有线程。
当一个程序运行时,内部可能包含多个顺序执行流,每个顺序执行流就是一个线程。
进程三大特性:独立性、动态性、并发性
独立性:进程是系统中独立存在的实体,它可以拥有自己的资源,每一个进程都拥有自己独有的地址空间。
在没有经过进程本身允许的情况下,一个进程用户不可以直接访问其他进程的地址空间。
动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。
在进程中加入了时间的概念,进程有自己的生命周期和各种不同的状态。
并发性:多个进程可以在单个处理器上并发执行。
总结:操作系统可以同时执行多个任务,每个任务就是进程;
进程可以同时执行多个任务,每个任务就是线程。
多线程便层的优点:
1.进程之间不能共享内存,线程之间非常容易共享内存;
2.系统创建进程需要为该进程重新分配系统资源,但创建线程代价小的多,因此使用多线程来实现多任务并发比多进程效率高。
3.Java语言内置了多线程功能支持,而不是单纯的作为底层操作系统的调度方式,从而简化了Java的多线程编程。
创建线程的方式:
1.继承Thread类
2.实现Runnable接口
3.使用Callable和Future创建线程
提供了一个call()方法作为线程执行体,但是call方法比run()方法强大。
1.call()方法有返回值
2.可以声明拋出异常
区别:第一种方法直接创建Thread的子类即可代表线程对象,
后者创建的Runnable对象只能作为线程对象的target
线程的生命周期
新建、就绪、运行、阻塞、死亡
新建: new Thread()
就绪: new Thread().start() //(表示线程可以开始运行了,具体合适运行取决于JVM里线程调度器的调用)
运行: run()
阻塞:
1.调用sleep()方法主动放弃所占用的处理器资源
2.线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
3.线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有
4.线程在等待某个通知(notify)
5.程序调用了线程的suspend()方法将该线程挂起。这个方法容易导致死锁,尽量避免使用。
当前正在执行的线程被阻塞之后,其他线程可以获得执行的机会。被阻塞的线程会在合适的时候重新进入就绪状态。
注意是就绪状态而不是运行状态。也就是说,被阻塞线程的阻塞解除后,必须重新等待线程调度器再次调度它。
解除阻塞:
1.调用slee()方法的线程经过了指定时间
2.线程调用的阻塞式IO方法已经返回
3.线程成功获得了视图取得的同步监视器
4.线程正在等待某个通知时,其他线程发出了一个通知
5.处于挂起状态的线程被调用了resume()恢复方法
死亡:
1.run()或call()方法执行完成,线程正常结束
2.线程抛出一个未不火的Exception或Error
3.直接调用该线程的stop()方法来结束该线程————容易导致死锁,不推荐
注意:调用线程使用start()方法,而不是run()方法。永远不要调用线程对象的run()方法!
调用start()方法来启动线程,系统会把run()方法当成线程执行体来处理;
但是如果直接调用线程的run()方法,则run()方法立即就会被执行,而且在run()方法返回之前
其他线程无法并发执行————也就是说,如果直接调用线程对象的run()方法,系统会把线程对象当成一个普通对象,
而run()方法也是一个普通方法,而不是线程执行体。
注意:只能对处于新建状态的线程调用start()方法,否则将引发IllegelThreadStateException异常。
注意:如果希望调用子线程的start()方法后子线程立即开始执行,程序可以使用Thread.sleep(1)来
让当前运行的线程(主线程)睡眠1毫秒————1毫秒就够了。因为CPU会在这1毫秒内去执行另一个
处于就绪状态的线程,这样就可以让线程立即执行。
注意:当一个线程开始运行后,不可能一直处于运行状态(除非线程瞬间就执行结束了),线程在运行过程中需要被中断,
目的是使其他线程获得执行的机会。对于采用抢占式策略的系统而言,系统会给每个执行的程序一个小时间段
来处理任务;当该时间段用完后,系统就会剥夺该线程所占用的资源,让其他线程获得执行的机会。
在选择下一个线程时,系统会考虑线程的优先级。
控制线程
1.join线程————让一个线程等待另一个线程完成
eg:三个线程,主线程与线程A并发执行,当自增长的变量i=20时,线程B也开始启动。
线程B中有JoinThread.join();则主线程要等线程B执行完才可以继续执行
join()方法有三种重载方式:
join():等待被join的线程执行完成
join(long millis):等待被join的线程的时间最长为millis毫秒。
如果在millis毫秒内被join的线程还没有执行结束,则不再等待
join(long millis,int nanos):(少用)等待最长时间为millis加nanos
2.后台线程
在后台运行的,为其他线程提供服务,又称为“守护线程”或“精灵线程”。eg:JVM的垃圾回收线程
特征:如果所有的前台线程都死亡,后台线程会自动运行
设置为后台进程:Thread.setDaemon(true)
当所有前台线程死亡时,后台线程随之死亡
3.线程睡眠————sleep()是静态方法
static void sleep(long millis):暂停当前执行线程mills毫秒,进入阻塞状态
static void sleep(long millis,int nanos):用的很少
4.线程让步:yield
yield():让正在执行的线程暂停,但不会阻塞线程。
将现场转入就绪状态,等待系统线程调度器重新调度。
注意:当线程调用yield()暂停后,只有优先级与当前线程相同或高于当前线程,才有获得执行的机会
sleep()与yield()方法区别:
1.sleep()暂停当前线程后,会给其他线程执行机会,不理会其他线程的优先级;
yield()方法只给优先级相同或更高级别的线程执行。
2.sleep() 会将策划程序转入阻塞状态,yield()不会
3.sleep() 方法声明跑出来InterruptedException异常,调用该方法时需要捕捉或抛出异常;
yield() 方法,没有声明抛出任何异常
4.sleep()方法比yield()方法有更好的移植性,通常不建议使用yield()方法来控制并发程序的执行
5.改变线程优先级
每个线程默认的优先级都与创建它的父线程的优先级相同,默认情况下,main线程具有普通优先级。
设置优先级:setPriority(int newPriority),newPriority的值在0~10之间
Thread类有3个静态常量: MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
6.传统的线程通信
为实现线程通信,可借助Object类的wait()、notify()、notifyAll()这三个方法,他们不属于Thread类。
这三个方法必须由同步监视器对象来调用,有以下两种情况:
1.对应synchronized修饰的同步方法,该类的默认实例(this)就是同步监视器,
可以在同步方法中直接调用这三个方法。
2.对于使用synchronized修饰的同步代码块,同步监视器是synchronized括号里面的对象,
所以必须使用该对象调用这三个方法。
wait()、notify()、notifyAll()三方法解释:
wait():导致当前线程等待,直到其他线程调用该同步监视器的notify()方法或notifyAll()方法来唤醒该线程。
wait()方法有三种形式————无时间参数的wait(一直等待,知道其他线程通知)、带有毫秒参数的wait()和带毫秒、毫微秒参数的wait()
(这两种方法都是等待指定时间后自动苏醒)。调用wait()方法的当前线程会释放对该同步监视器的锁定。
notify():唤醒在此同步监视器上等待的单个线程。如果所有线程都在此同步监视器上等待,则会选择唤醒其中一个线程。选择是任意的。
notifyAll():唤醒在此同步监视器上等待的所有线程。