java线程的创建与结束
/**
* 使用软件时运行软件的程序/可执行文件,操作系统将程序加载到内存中产生进程
* 每个进程都是内存中一块独立运行的程序空间。每个进程由cpu资源、data数据、code代码三部分组成。cpu通过时间片轮转轮流处理每个进程
* 线程为进程中的执行单元(单一的顺序控制流程),进程中至少有一个线程,可拥有多个并行的concurrent线程。
* 同一个进程中的多个线程共享内存地址空间,可以访问相同的变量、对象,从同一堆heap中分配对象并进行通信、数据交换和同步操作
* 相比进程,线程之间信息传递容易,线程的启动、中断、消亡、消耗的资源非常少
* 操作系统为class的main方法创建通往cpu的通道为主线程,子线程通过主线程创建
*/
public class Test1Thread extends Thread {
//实现多线程方法之一:通过继承Thread类来创建子线程
@Override
public void run() {
//重写run方法,当子线程启动时会执行run方法,将交给子线程执行的内容放在run方法中
System.out.println("子线程:"+this.getName()+"开始运行");
//Thread类中的方法.getName()返回当前线程的引用/名字,并非变量名
for (int i = 0;i<20;i++){
System.out.println("子线程:"+this.getName()+" 进度:"+i*5+"%");
}
System.out.println("子线程:"+this.getName()+"运行结束");
}
public Test1Thread(){
System.out.println("创建子线程:"+this.getName());
}
public static void main(String[] args) {
//通过主线程创建子线程
System.out.println("主线程开始运行");
Test1Thread t1 = new Test1Thread();
t1.start();
//启动子线程需要通过.start()方法,之后子线程会自行执行run方法,如果直接调用run方法会变成主线程执行对象的方法
Test1Thread t2 = new Test1Thread();
//生成多个子线程
t2.start();
System.out.println("主线程结束");
//主线程会在启动两个子线程之后结束,此时子线程任然在执行中,主线程是进程中最先开始的但并不一定是最后结束的
/*
结果为:
主线程开始运行
创建子线程:Thread-0
创建子线程:Thread-1
主线程结束
子线程:Thread-0开始运行
子线程:Thread-1开始运行
子线程:Thread-0 进度:0%
子线程:Thread-1 进度:0%
子线程:Thread-1 进度:5%
子线程:Thread-0 进度:5%
子线程:Thread-0 进度:10%
...
子线程:Thread-1 进度:95%
子线程:Thread-0 进度:70%
子线程:Thread-1运行结束
子线程:Thread-0 进度:75%
...
子线程:Thread-0 进度:95%
子线程:Thread-0运行结束
主线程在创建完子线程后结束,cpu通过时间片轮转轮流处理每个线程,所以线程交替打印内容,操作系统分配给线程的时间片不是固定长度,所以有的线程先完成
*/
}
}
class Test2Thread implements Runnable{
//实现多线程方法之二:实现Runnable接口
@Override
public void run() {
//Runnable接口中只有一个run()方法,Thread类同样实现了Runnable接口的run()方法
System.out.println("子线程:"+Thread.currentThread().getName()+"开始运行");
//实现Runnable接口的Test2Thread类并不是线程类,通过Thread类静态方法.currentThread()获取当前执行run方法的线程对象,通过线程对象.getName()获取线程对象的String name属性的值,所以不会返回引用变量的名字
System.out.println("子线程:"+Thread.currentThread().getName()+"运行结束");
}
public Test2Thread(){
System.out.println(this);
//在构造器中this指当前正在构造的对象
System.out.println(Thread.currentThread().getName());
//这里得到的是正在构造Test2对象的线程
}
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
//结果为:main
//主线程的name是main
Thread t1 = new Thread(new Test2Thread());
//Thread(Runnable)构造方法传入Runnable的实现类对象,这个线程在执行run方法时会调用实现类的run方法
t1.start();
//通过实现Runnable可以解决继承其他类和继承Thread,继承位不足的问题
Thread t2 = new Thread(new Test2Thread());
t2.start();
System.out.println(Thread.currentThread().getName()+"结束");
/*
结果为
Test2Thread@1b6d3586
main
Test2Thread@4554617c
main
main结束
子线程:Thread-1开始运行
子线程:Thread-0开始运行
子线程:Thread-0运行结束
子线程:Thread-1运行结束
.start()方法只是将线程从新生状态变为就绪状态
线程的生命周期需要经历5个阶段。
新生状态new:new Thread()会进入新生状态
就绪状态runnable:所有排队等待获得cpu的线程都处于就绪状态/可运行状态。1.对新生状态的线程执行.start()会变为就绪状态2.从阻塞状态结束后回到就绪状态3.运行状态执行完分配的时间片后JVM将cpu资源从本线程切换到其他线程会回到就绪状态4.运行状态中run方法执行到yield()方法会立即回到就绪状态
运行状态running:获得cpu资源执行run方法,每次执行都从上一次执行完的代码的下一代码开始往下进行
阻塞状态blocked:在运行状态时执行run方法中因为某些原因无法向下执行代码,线程进入阻塞状态JVM将cpu资源切换到其他线程。1.Thread.sleep(long ms)使当前线程休眠,进入阻塞状态,指定时间到后进入就绪状态2.执行.wait()方法使进入阻塞状态,需要等待notify()方法唤醒notify通知3.执行io操作等会阻塞的操作,当阻塞的原因消失(.read等待数据返回.write等待数据写入)后进入就绪状态4.join()线程联合,等待另一个线程执行结束才能继续执行5.同步锁synchronized
死亡状态terminated:线程完成了run方法或者被stop()destroy()强制终止后进入死亡状态,无法回到其他状态
*/
}
}
class Test3Thread implements Runnable{
//测试线程的结束
boolean notFinish = true;
//设置一个标记,当需要结束线程时将标记变为false
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始");
for (int i=0;notFinish;i++){
System.out.println("执行次数:"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName()+"结束");
}
public void over(){
notFinish=false;
//结束线程的方法
}
public static void main(String[] args) {
System.out.println("主线程开始");
Test3Thread t3 = new Test3Thread();
new Thread(t3).start();
//子线程进入就绪状态,如果这时调用over()方法子线程在进入运行状态后不会执行循环。如果要通过主线程关闭子线程并且让子线程执行一段时间后再结束,主线程就不能过早结束,可以让主线程阻塞来延长生命周期
try {
System.in.read();
//System.in.read()会等待用户输入,单次read()只会返回第一个字节,但目的不是读取内容而是利用io操作造成阻塞,当用户随意输入内容回车之后主线程向下执行
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
System.in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
t3.over();
System.out.println("主线程结束");
/*
主线程开始
Thread-0开始
执行次数:0
执行次数:1
执行次数:2
主线程结束
Thread-0结束
主线程调用over()后,子线程停止循环,将循环外/下面的语句执行完毕后安全结束
*/
}
}