第一章 java多线程技能
1.1进程和多线程的概念
进程是操作系统的基础,当一个程序载数据集合运行时的集合,线程则是进程中独立运行的子任务。比如QQ.exe运行的时候 你可以同时聊天 还有视频。
1.1.1使用多线程的好处
同样的俩个任务,任务1和任务2如果在单任务的环境中,需要一个任务任务的去执行,而在多任务的情况中 我可以俩个任务同时执行,而且可以在俩个任务中进行随意切换。
这样使用多线程的好处显而易见,使CPU的利用率大大的得到提升。
1.2 多线程的使用
1.2.1实现多线程
在java的jdk开发包中,已经自带了多线程技术的支持。可以很方便地进行多线程编程。实现多线程编程有俩种,一种使继承 Thread类,一种是实现Runnable接口。
在学习之前我们先来看看Thread类的源码:
public class Thread implements Runnable
从上面可以看到 Thread类其实是实现了Runnable的接口,它俩的唯一区别 就是 因为JAVA是单继承,所以不支持多继承,那么当一个类已经有父类的时候,就无法用继承Thread类的方法 实现多线程,那么实现接口就是实现多线程的方法
下面是俩种多线程的实现方式:
/**
* 创建yourThred类继承 Thread类 然后重写run方法 ,在run方法中写多线程执行的语句
*/
class YourThread extends Thread{
private int count = 5;
public YourThread(String name){
super();
this.setName(name);
}
@Override
public void run() {
super.run();
while (count>0){
count--;
System.out.println(this.getName());
}
}
}
public class MyThread {
public static void main(String[] args) {
YourThread yourThread1 = new YourThread("线程A");
yourThread1.start();
System.out.println("多线程学习");
}
}
/**
* 创建yourThred类实现 Runnable接口 然后实现run方法 ,在run方法中写多线程执行的语句
* 然后再main方法中 通过Thread的构造方法 去创建一个Thread类
*/
class YourThread implements Runnable{
private int count = 5;
@Override
public void run() {
System.out.println("多线程学习");
}
}
public class MyThread {
public static void main(String[] args) {
YourThread yourThread = new YourThread();
Thread thread = new Thread(yourThread);
thread.start();
}
}
1.3 实例变量 和线程安全
以下俩段代码诠释了 线程共用一个实例变量,和不共用的区别
/**
*在这段代码中 一共创建了二个线程类,所以每个类都有自己的实例变量,就不会存在俩个线程操控一个实例变量的情况
*/
class YourThread extends Thread{
private int count = 5;
public YourThread(String name){
super();
this.setName(name);
}
@Override
public void run() {
while(count>0){
count--;
System.out.println(this.currentThread().getName()+""+count);
}
}
}
public class MyThread {
public static void main(String[] args) {
YourThread yourThread1 = new YourThread("线程A");
YourThread yourThread = new YourThread("线程B");
yourThread.start();
yourThread1.start();
}
}
/**
*在这段代码中 创建了一个线程子类,然后创建俩个线程类,运行这俩个线程类 会共用同一个实例变量 所以导致
* 实例变量的值会出现重复的值,即俩个线程同时对实例进行了操控,产生了线程不安全
*/
class YourThread extends Thread{
private int count = 5;
public YourThread(String name){
super();
this.setName(name);
}
@Override
public void run() {
while(count>0){
count--;
System.out.println(this.currentThread().getName()+" "+count);
}
}
}
public class MyThread {
public static void main(String[] args) {
YourThread yourThread1 = new YourThread("线程A");
Thread thread = new Thread(yourThread1);
Thread thread1 = new Thread(yourThread1);
thread.start();
thread1.start();
}
}
1.4 currentThread方法
currentThread() 方法返回代码段正在被哪个线程调用的那个线程的信息,也是Thread类中的一个静态方法。
class thread extends Thread{
public thread(){
System.out.println(Thread.currentThread().getName());
}
@Override
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
public class MyThread {
public static void main(String[] args) {
thread Thread1 = new thread();
Thread1 .start();
}
}
main
Thread-0
从上图可知第一次有main线程调用thread的构造方法所以获取的名字是main,然后创建的线程 启动 会获取到新线程的名称
这里还需要弄清楚俩个概念:
- Thread.currentThread().getName():代表代码段正在被哪个线程调用的那个线程的信息
- this.getName():表示创建的线程实例本身
1.5 isAlive() 方法
isAlive() 方法的功能是判断当前线程是否处于活动状态
活动状态就是线程已经启动(start)且尚未终止。线程处于正在运行(调用了run)或者准备开始运行的状态,就认为线程是存活的
class thread extends Thread{
@Override
public void run(){
System.out.println(this.isAlive());
}
}
public class MyThread {
public static void main(String[] args) {
thread Thread1 = new thread();
System.out.println("begin: "+Thread1.isAlive());
Thread1 .start();
System.out.println("end: "+Thread1.isAlive());
}
}
begin: false
end: true
true
第一个由于处于新建状态,还没有调用 start 方法启动,因此不是处在活动状态
第二个因为在 run 方法中,说明已经启动了,且处于运行状态,因此也是处于活动状态
第三个因为线程还没有终止,此时线程还是处于活动状态
注意:如果线程是睡眠状态 那么isAlive()方法也会返回false;
1.6 sleep()方法
sleep() 的作用是在指定的毫秒数内让当前“正在执行的线程”休眠,即暂停执行,这个“正在执行的线程”指的是 this.currentThread() 返回的线程
1.7 getId() 方法
getId() 方法的作用是取得线程的唯一标识
public class MyThread {
public static void main(String[] args) {
thread Thread1 = new thread();
thread Thread2 = new thread();
System.out.println(Thread1.getId());
System.out.println(Thread2.getId());
}
}
结果为:
12
13
1.8 停止线程
在 Java 中有以下 3 种方法可以终止正在运行的线程:
- 使用退出标志,使线程正常退出,也就是当 run() 方法完成后线程中止。
- 使用 stop() 方法强行终止线程,但是不推荐使用这个方法,该方法已被弃用。
- 使用 interrupt 方法中断线程。
1.8.1 使用标志位终止线程
在 run() 方法执行完毕后,该线程就终止了。但是在某些特殊的情况下,run() 方法会被一直执行;比如在服务端程序中可能会使用 while(true) { … } 这样的循环结构来不断的接收来自客户端的请求。此时就可以用修改标志位的方式来结束 run() 方法。可以通过在run方法中 抛出异常 这样最好终止线程了
1.8.2 使用 stop() 终止线程
虽然 stop() 方法确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且该方法已被弃用,最好不要使用它
为什么弃用stop:
- 调用 stop() 方法会立刻停止 run() 方法中剩余的全部工作,包括在 catch 或 finally 语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。
- 调用 stop() 方法会立即释放该线程所持有的所有的锁,导致数据得不到同步,出现数据不一致的问题。
1.8.3 使用 interrupt() 中断线程
现在我们知道了使用 stop() 方式停止线程是非常不安全的方式,那么我们应该使用什么方法来停止线程呢?答案就是使用 interrupt() 方法来中断线程。
需要明确的一点的是:interrupt() 方法并不像在 for 循环语句中使用 break 语句那样干脆,马上就停止循环。调用 interrupt() 方法仅仅是在当前线程中打一个停止的标记,并不是真的停止线程。
也就是说,线程中断并不会立即终止线程,而是通知目标线程,有人希望你终止。至于目标线程收到通知后会如何处理,则完全由目标线程自行决定。这一点很重要,如果中断后,线程立即无条件退出,那么我们又会遇到 stop() 方法的老问题。
事实上,如果一个线程不能被 interrupt,那么 stop 方法也不会起作用。