进程和线程
一个程序就是一个进程,而一个程序中的多个任务则被称为线程。
进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位。
使用多线程
在Java的JDK开发包中,已经自带了对多线程技术的支持,可以很方便地进行多线程编程。实现多线程编程的方式有两种,一种是继承 Thread 类,另一种是实现 Runnable 接口。使用继承 Thread 类创建线程,最大的局限就是不能多继承,所以为了支持多继承,完全可以实现 Runnable 接口的方式。需要说明的是,这两种方式在工作时的性质都是一样的,没有本质的区别。
Thread.java 类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用 Thread 中的 run() 方法,也就是使线程得到运行,多线程是异步的,线程在代码中启动的顺序不是线程被调用的顺序
- Runnable 接口
/**
* 线程学习
* @ClassName MyThread
* @Description TODO
* @Author lijun
* @Date 2020/7/17 9:25
*/
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("我的第一个线程");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
- 继承 Thread 类
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我的第一个线程");
}
}).start();
}
- Thread(): 分配新的 Thread 对象。
- Thread(Runnable target): 分配新的 Thread 对象。
- Thread(Runnable target, String name): 分配新的 Thread 对象。
- Thread(String name): 分配新的 Thread 对象。
- Thread(ThreadGroup group, Runnable target): 分配新的 Thread 对象。
- Thread(ThreadGroup group, Runnable target, String name): 分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,并作为 group 所引用的线程组的一员。
- Thread(ThreadGroup group, Runnable target, String name, long stackSize): 分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,作为 group 所引用的线程组的一员,并具有指定的堆栈大小。
- Thread(ThreadGroup group, String name): 分配新的 Thread 对象。
实例变量与线程安全
自定义线程类中的实例变量针对其他线程可以有共享与不共享之分。当每个线程都有各自的实例变量时,就是变量不共享。共享数据的情况就是多个线程可以访问同一个变量。
上代码:
public class MyThread implements Runnable {
private int count = 5;
@Override
public void run() {
count--;
System.out.println("线程"+Thread.currentThread().getName()+" 计算 count = "+count);
}
}
public class Test{
/**
* 线程C,B的打印结果都是3,说明C和B同时对count进行了处理,产生了“非线程安全问题”。而我们想
* 要的得到的打印结果却不是重复的,而是依次递减的
*
* @Description:
* @Param: [args]
* @Return: void
* @Author: lijun
* @Date: 2020/8/3
**/
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread a = new Thread(myThread,"A");//线程A 计算 count = 2
Thread b = new Thread(myThread,"B");//线程B 计算 count = 3
Thread c = new Thread(myThread,"C");//线程C 计算 count = 3
a.start();
b.start();
c.start();
}
}
多线程方法
- currentThread()方法:返回代码段正在被线程调用的信息。
- isAlive()方法:判断当前的线程是否处于活动状态
- sleep()方法:在指定的毫秒数内让当前”正在执行的线程”休眠(暂停执行)。这个”正在执行的线程”是指this.currentThread()返回的线程
- getId()方法:获取唯一标识
#5、停止线程
停止线程是在多线程开发时很重要的技术点。停止线程并不像break语句那样干脆,需要一些技巧性的处理。
在Java中有以下3种方法可以终止正在运行的线程:
1)使用退出标志,使线程正常退出,也就是当run()方法完成后线程停止。
2)使用stop()方法强行终止线程,但是不推荐使用这个方法,因为该方法已经作废过期,使用后可能产生不可预料的结果。
3)使用interrupt()方法中断线程。 - 暴力法停止线程:调用stop()方法停止线程。但是会抛出java.lang.ThreadDeath 异常,但在通常的情况下,此异常不需要显示地捕捉。所以stop()方法已经被作废,因为如果强制让线程停止线程则有可能使一些清理性的工作得不到完成。另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的情况。
- 异常法停止线程:使用interrupt()方法并不会真正的停止线程,调用interrupt()方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。测试当前线程是否已经中断,有两个方法。
1)interrupted() :不止可以判断当前线程是否已经中断,而且可以会清除该线程的中断状态
2)isInterrupted() :只会判断当前线程是否已经中断,不会清除线程的中断状态。
注意:如果线程在sleep()状态下被停止,也就是线程对象的run()方法含有sleep()方法,在此期间又执行了thread.interrupt() 方法,则会抛出java.lang.InterruptedException: sleep interrupted异常,提示休眠被中断。 - return法停止线程:只需要把异常法中的抛出异常更改为return就行了。
public class MyThread extends Thread {
@Override
public void run() {
for (int i=0; i<50000; i++){
if (this.isInterrupted()) {
System.out.println("已经是停止状态了!");
return;//替换此处
}
System.out.println(i);
}
System.out.println("不进行return,我会被执行的哦!");
}
}
暂停线程
- 可以使用suspend暂停线程,resume恢复线程执行。
如果加了sync关键字,暂停线程后没有恢复,可能导致公共资源无法访问。没加关键字,暂停之后可能出现数据不一致。
yield()方法
放弃当前的cpu资源,让其他的任务去占用cpu。
-
线程的优先级
设置线程可以使用setPriority(),在java中,线程分了1-10个等级。
- 优先级是可以继承的。如果是a线程启动的b线程,那b的线程与a的线程优先级一样的。
- cpu会把资源让给优先级比较高的线程。
-
守护线程
线程分两种,一种是用户线程,一种是守护线程。
- 守护线程守护一个非守护线程,当不存在非守护线程了,守护线程字段销毁。
- 典型的守护线程如垃圾回收线程。