Java多线程基础
进程和多线程
进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。
程序:指令序列,用来让cpu完成指定的任务,一个.java程序经过编译形成.class文件,操作系统创建一个JVM虚拟机进程(每执行一次main方法就创建一个JVM进程),并在虚拟机中加载class文件并运行,进程通过创建你新线程来执行具体的任务。
线程:线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
创建多线程
继承Thread类
分析线程信息
使用jdk中的命令分析线程信息
- jps+jstack.ext
- jmc.exe
- jvisualvm.exe
实现Runnable接口
Thread.java类的8个构造方法
- Thread()
分配一个新的 Thread对象。 - Thread(Runnable target)
分配一个新的 Thread对象。 - Thread(Runnable target, String name)
分配一个新的 Thread对象。 - Thread(String name)
分配一个新的 Thread对象。
构造函数Thread(Runnable target) 不仅可以传入Runnable接口对象,而且可以传入一个Thread类的对象。
线程安全问题
在run()方法前添加synchronized关键字,一个线程调用run方法前会判断run方法有没有上锁,如果run方法被上锁,说明其他线程正在调用,必须等待。
synchronized可以对任意对象及方法加锁,被加锁的这段代码称为“互斥区”或“临界区”。
常用方法
currentThread()
返回代码正在被哪个线程调用
run2的构造函数是被main线程调用,run方法是被Thread-0的线程调用
执行方法run()和start()方法的区别:
- run方法:立即执行run方法,不启动新线程。
- start方法:执行run方法的时机不确定,启动新线程。
isAlive()
isAlive方法判断当前线程是否存活
sleep(long millis)
在指定的时间(毫秒)内让当前正在执行的线程休眠,这个当前正在执行的线程是指this.currentThread()返回的线程。
sleep(long millis,int nanos)
在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠
StackTranceElement[ ] getStackTrace()
作用是返回一个表示该线程堆栈跟踪元素数组
static void dumpStack()
将当前线程的堆栈跟踪信息输出到标准错误流
getID()
用于取得线程的唯一标识
停止线程
interrupt() 方法
调用interrupt方法仅仅在当前线程中做了一个停止的标记,并不是真正的停止线程
判断线程是否停止
- public static boolean interrupted() :测试currentThread()是否已经中断,执行后具有清除状态标志值为false的功能。也可以用Thread.interrupted()判断,因为在Thread.java类中调用静态static方法时,大多数是针对currentThread()线程进行操作的
- public boolean this.isIntererupted() :测试this关键字所在类的对象是否已经中断,非静态方法,作用与调用这个方法的对象。不清楚状态标志。
interrupt()方法中断线程
public class MyThread5 extends Thread {
@Override
public void run() {
super.run();
try{
for (int i = 0; i <500000 ; i++) {
if(this.isInterrupted()){
System.out.println("已经是停止状态,我要推出了!");
throw new InterruptedException();
//或者不抛异常,直接return
}
System.out.println("i="+(i+1));
}
}
catch (InterruptedException e){
System.out.println("进入run方法中的catch了!!");
e.printStackTrace();
}
}
public static void main(String[] args) {
try{
MyThread5 myThread5 = new MyThread5();
myThread5.start();
Thread.sleep(2000);
myThread5.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch!!");
e.printStackTrace();
}
System.out.println("end!!!");
}
}
sleep和interrupt
不管调用顺序,只要interrupt()和sleep()方法碰到一起就会出现异常:在sleep状态执行interrupt()方法会出现异常;调用interrupt()方法给线程打上了中断标记,在执行sleep()方法也会出现异常
使用stop() 方法暴力停止线程(不推荐)
public class MyThread6 extends Thread {
private int i=0;
@Override
public void run() {
super.run();
try {
while (true){
i++;
System.out.println("i="+i);
Thread.sleep(1000);
}
}
catch (InterruptedException e){
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
MyThread6 myThread6 = new MyThread6();
myThread6.start();
Thread.sleep(8000);
myThread6.stop();
}
}
暂停线程
在Java多线程中,可以使用suspend()方法暂停线程,使用resume()方法来恢复线程的执行
suspend()、resume() 方法是过期作废的方法,原因是容易造成公共同步对象被独占,其他线程无法访问公共同步对象的结果,因此想要实现对线程进行暂停与恢复的处理,可以使用wait()、notify()或者notifyAll()方法
System.out.println也是同步方法
yield方法
yield方法的作用是放弃当前的cpu资源,让其他任务去占用cpu执行时间,放弃的时间不确定,有可能刚刚放弃,马上又获得cpu时间片
线程优先级
在Java中线程的优先级分为1-10共10个等级,如果线程优先级小于1或者大于10,则JDK抛出异常 throw new IllegalArguementException()
- 线程优先级的继承特性
在Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级和A线程的优先级是一样的
public class MyThread10 extends Thread {
@Override
public void run() {
super.run();
System.out.println("MyThread10 run priority="+this.getPriority());
}
}
public class MyThread9 extends Thread {
@Override
public void run() {
super.run();
System.out.println("MyThread9 run priority="+this.getPriority());
MyThread10 myThread10 = new MyThread10();
myThread10.start();
}
public static void main(String[] args) {
System.out.println("main thread begin priority="+Thread.currentThread().getPriority());
//Thread.currentThread().setPriority(6);
System.out.println("main thread end priority="+Thread.currentThread().getPriority());
MyThread9 myThread9 = new MyThread9();
myThread9.start();
}
}
去掉注释
2. 优先级的规律
高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完,CPU尽量将执行资源让给优先级比较高的线程,线程的优先级与执行顺序具有不确定性、随机性
守护线程
Java中有两种线程,一种是用户线程,另一种是守护线程
守护线程是一种特殊线程,当进程中不存在用户线程了,则守护线程自动销毁,典型的守护线程是垃圾回收线程
只用当最后一个用户线程结束时,则守护线程才随着JVM一同结束,守护线程的作用是为其他运行的线程提供服务。
public class MyThread11 extends Thread {
private int i=0;
@Override
public void run() {
super.run();
try{
while(true){
i++;
System.out.println("i="+(i+1));
Thread.sleep(1000);
}
}
catch (InterruptedException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
try{
MyThread11 myThread11 = new MyThread11();
myThread11.setDaemon(true);
myThread11.start();
Thread.sleep(5000);
System.out.println("main 结束,守护线程也随之结束!");
}
catch (InterruptedException e){
e.printStackTrace();
}
}
}