目录
一、概述
1、进程与线程的关系
进程是一个应用程序,一个进程就是一个软件;
线程是一个进程中的执行单元,一个进程可以启动多个线程;
如果把一个公司比作一个进程,公司的保洁阿姨是一个线程,公司的生产部是一个线程,公司的销售部是一个线程
2、多线程并发
假设启动了10个线程,内存中就会有10个"栈"空间,每个栈与每个栈之间互不干扰,各自执行个自的,这就是多线程并发;
真正的多线程并发:
t1线程执行t1的;
t2线程执行t2的;
t1不会影响t2,t2也不会影响t1。这就叫做多线程并发;
例如:4核CPU表示在同一个时间点上,可以真正的有四个进程并发执行
单核CPU表示只有一个大脑,不能够做到真正的多线程并发,但是可以做到给人一种多线程并发的感觉;
注意:使用多线程并发时,是有可能支线程还没结束主线程就已经结束的现象
例如:以下代码只有一个main线程
public class Test01 {
public static void main(String[] args) {
System.out.println("主线程开始");
m1();
System.out.println("主线程结束");
}
private static void m1() {
System.out.println("m1开始");
m2();
System.out.println("m1结束");
}
private static void m2() {
System.out.println("m2开始");
m3();
System.out.println("m2结束");
}
private static void m3() {
System.out.println("m3运行");
}
}
二、实现线程的两种方式
1、实现线程的第一种方式
编写一个类,直接继承java.lang.Thread,然后重写run()方法;
主类:
public class Test01 {
public static void main(String[] args) {
//这是main方法,这里的代码属于主线程,在主栈中运行
//创建一个分支线程对象
MyThread myThread=new MyThread();
//调用start()方法启动线程
//这个语句执行瞬间线程启动后语句立马结束,然后向下执行语句
myThread.start();
//以下语句还是在主线程中
for(int i=1;i<100;i++){
System.out.println("主栈--->"+i);
}
}
}
支栈类:
//创建类继承Thread
public class MyThread extends Thread{
//重写run()方法
public void run(){
//这里面的语句就会运行在支栈中,和main线程不相干
for(int i=1;i<100;i++){
System.out.println("支栈--->"+i);
}
}
}
运行结果是两个线程同时启动:
2、实现线程的第二种方式
编写一个类,实现java.lang.Runnable.接口;
主类:
public class Test01 {
public static void main(String[] args) {
//创建一个MyRunnable可运行对象
MyRunnable myRunnable=new MyRunnable();
//将可运行对象封装成一个线程对象
Thread thread=new Thread(myRunnable);
//启动线程
thread.start();
//以下语句还是在主线程中
for(int i=1;i<100;i++){
System.out.println("主栈--->"+i);
}
}
}
支栈类:
//编写类实现Runnable接口
public class MyRunnable implements Runnable{
//实现run方法
@Override
public void run() {
//这里面的语句就会运行在支栈中,和main线程不相干
for(int i=1;i<100;i++){
System.out.println("支栈--->"+i);
}
}
}
运行结果交替输出:
采用匿名内部类方式实现线程:
public class Test01 {
public static void main(String[] args) {
//创建线程对象
Thread t=new Thread(new Runnable() {
@Override
public void run() {
//这里面的语句就会运行在支栈中,和main线程不相干
for(int i=1;i<100;i++){
System.out.println("支栈--->"+i);
}
}
});
//启动线程
t.start();
//以下语句还是在主线程中
for(int i=1;i<100;i++){
System.out.println("主栈--->"+i);
}
}
}
run()和start():
run是不需要手动调用的,由JVM线程调度机制来运作的
start()方法栈帧:开辟一个新的栈空间称为分支栈,只要开完,方法就结束;
三、线程生命周期
1、新建状态
也就是刚new出来的线程对象
2、就绪状态
就绪状态又叫做可运行状态,表示当前线程有抢夺CPU时间片的权利,当一个线程抢夺到时间片之后,就开始执行run方法。
3、运行状态
run方法执行就进入了 运行状态,当之前抢夺的时间片用完之后就会重新回到就绪状态,继续参与抢夺时间片;
4、阻塞状态
当一个线程遇到阻塞事件,或者使用sleep方法,线程就会进入阻塞状态,阻塞状态会放弃线程之前占有的时间片;
阻塞解除需要再次回到就绪状态参与抢夺时间片;
5、死亡状态
四、获取线程的名字
获取线程名字:
使用getName();方法
public class Test01 {
public static void main(String[] args) {
//创建线程对象
Thread thread=new MyThread();
//获取线程名字
String threadName=thread.getName();
System.out.println(threadName);//Thread-0
}
}
修改线程名字:
使用setName()方法
public class Test01 {
public static void main(String[] args) {
//创建线程对象
Thread thread=new MyThread();
//修改线程名字
thread.setName("zhangsan");
//获取线程名字
String threadName=thread.getName();
System.out.println(threadName);//zhangsan
}
}
五、获取当前线程对象
使用currentThread()来获取当前对象
支栈类:
public class MyThread extends Thread{
//重写run()方法
public void run(){
//这里面的语句就会运行在支栈中,和main线程不相干
for(int i=1;i<100;i++){
//下面的“t“就是当前线程
//若是t1线程执行run()方法t就是t1线程
//若是t2线程执行run()方法t就是t2线程
Thread t=Thread.currentThread();
System.out.println(t.getName()+"-->"+i);
}
}
}
主类:
public class Test01 {
public static void main(String[] args) {
//创建线程对象
Thread t1=new MyThread();
Thread t2=new MyThread();
//修改线程名字
t1.setName("t1");
t2.setName("t2");
//启动线程
t1.start();
t2.start();
}
}
运行结果:
六、线程的sleep方法
1、static void sleep(long millis)方法
2、参数是毫秒
3、作用,让当前线程进入休眠,就是进入阻塞状态。
例如:
public class Test01 {
public static void main(String[] args) {
//创建线程对象
Thread t1=new MyThread();
Thread t2=new MyThread();
//修改线程名字
t1.setName("t1");
t2.setName("t2");
//启动线程
t1.start();
//间隔5秒在往下执行
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//t1启动5秒后t2才开始启动
t2.start();
}
}
七、终止线程的休眠
使用interrupt()来终止
例如:
支类:
public class MyThread extends Thread{
//重写run()方法
public void run(){
//休眠一个小时
try {
Thread.sleep(1000*60*60);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=1;i<100;i++){
//下面的“t“就是当前线程
//若是t1线程执行run()方法t就是t1线程
//若是t2线程执行run()方法t就是t2线程
Thread t=Thread.currentThread();
System.out.println(t.getName()+"-->"+i);
}
}
}
主类:
public class Test01 {
public static void main(String[] args) {
//创建线程对象
Thread t1=new MyThread();
//修改线程名字
t1.setName("t1");
//启动线程
t1.start();
//间隔5秒在往下执行
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒线程
t1.interrupt();
}
}
八、强行终止线程的执行
第一种方法
使用stop();
public class Test01 {
public static void main(String[] args) {
//创建线程对象
Thread t1=new MyThread();
//修改线程名字
t1.setName("t1");
//启动线程
t1.start();
//间隔5秒在往下执行
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//强行终止t1线程的执行
t1.stop();
}
}
注意:这种方法直接将线程杀死,线程没有保存的数据会丢失
第二种方法
例如:
主类:
public class Test01 {
public static void main(String[] args) {
//创建对象
MyThread thread=new MyThread();
//创建线程对象
Thread t=new Thread(thread);
//启动线程
t.start();
//间隔5秒在往下执行
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//强行终止t1线程的执行
thread.run=false;
}
}
支类:
public class MyThread implements Runnable{
//运行标记
boolean run=true;
//重写run()方法
public void run(){
for(int i=1;i<100;i++){
//判断,如果想要终止就在外部修改run的值为false
if (run) {
System.out.println("支栈-->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//终止,想要在终止前运行的代码可以写在此处
System.out.println("数据已保存!");
return;
}
}
}
}
运行结果: