目录-Java中的线程
1)通过继承Thread类,并重写run方法,实例化该类对象
1)当手中有一个Thread对象时,调用其start()方法启动线程。
2)怎么理解t.start()在做什么?【主线程和子线程出现先后】
3)注意:一个线程调用过t.start()不能再调用+不能调成run()
1)Thread.sleep(...);—让线程休眠...毫秒
一、线程—独立的执行流
1.Java中的线程在代码中是如何体现的?
java.lang.Thread类(包括其子类)的一个对象
2.如何在代码中创建线程(基本)
Thread—线程;Runnaable—让这个线程去完成的工作(任务)
1)通过继承Thread类,并重写run方法,实例化该类对象
得到了Thread对象
【继承Thread,说明还是thread,只是通过重写让thread有了具体的工作了】
package com.wy.how_to_creat_thread_instance.demo1;
/**
* 继承java.lang.Thread类
* 光继承的情况,这个线程中一条指令都没有,所以需要给线程书写让他执行的指令(以语句形式体现)
* 通过重写run方法
*/
public class MyFirstThreadClass extends Thread{
@Override
public void run() {
//这个方法下写的所有代码,如果正确创建线程的话,都会运行在新的线程执行流中
System.out.println("这是我的第一个线程");
}
}
package com.wy.how_to_creat_thread_instance.demo1;
/**
* 用来实例化对象
*/
public class Main {
public static void main(String[] args) {
MyFirstThreadClass t=new MyFirstThreadClass();
//t指向了创建出来的线程对象
//线程创建好了,但并没有运行起来
}
}
2)通过实现Runnable接口,重写run方法
实例化Runnable对象,利用Runnable对象去构建一个Thread对象
【构建一个Runnable对象即构建了个任务了,拿着任务去构建thread对象】
package com.wy.how_to_creat_thread_instance.demo2;
/**
* 任务
* 1.实现java.lang.runnable接口,实际效果与继承接口是一样的
* 2.通过重写run方法,来指定任务要做的工作
* 这与这个任务要不要交给一个新的线程去执行,我们是不知道的
*/
public class MyFirstTask implements Runnable{
@Override
public void run() {
System.out.println("这是我的第一个任务的第一句话");
}
}
package com.wy.how_to_creat_thread_instance.demo2;
public class Main {
public static void main(String[] args) {
MyFirstTask task=new MyFirstTask();//创建了一个任务对象
Thread t=new Thread(task);//把task作为Thread的构造方法传入
//让新创建的Thread对象去执行task任务
//语句就运行在新的线程中
//这里只是创建出线程,暂时还没有运行
}
}
总结:
第一种方法视为直接创建了一个线程对象
第二个方法是创建任务,再把任务分配给一个线程注意,此时main方法执行是没有输出的,这里只是简单创建了线程。
3.启动线程
1)当手中有一个Thread对象时,调用其start()方法启动线程。
注意:
1.一个已经调用过start(),不能再调用,再调用会有异常发生
2.千万不要调用成run()
package com.wy.how_to_start_thread.demo1;
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("正在的执行起来");
}
}
package com.wy.how_to_start_thread.demo1;
public class Main {
public static void main(String[] args) {
MyThread t=new MyThread();
//通过调用Thread对象的start方法,来开始线程的运行,线程中的代码运行起来
t.start();
}
}
2)怎么理解t.start()在做什么?【主线程和子线程出现先后】
a.观察现象:
package com.wy.how_to_start_thread.demo2;
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("我是MyThread类下的run方法中的语句,会运行在子线程中");
}
}
顺序实际上是不确定的。
b.原因解释:
线程状态:新建——就绪——运行——结束。
t.start()只做了一件事,就是把线程的状态从新建变成了就绪,线程被加入到线程调度器(不区分是OS还是JVM实现的)的就绪队列中,等待被调度器选中分配CPU(还未分配)。
从子线程进入就绪队列这一刻起,子线程和主线程在地位上就完全平等了,所以哪个线程先被选中去分配CPU,就完全随机了。但是大概率是主线程语句先执行,因为t.start()是主线程的语句,换言之,这条语句被执行了说明主线程正在CPU上(主线程是运行状态),而主线程刚刚执行完t.start()就马上发生线程调度的概率不大,因此大概率还是去执行t.start()的下一条语句。
c.何时子线程的语句先执行?
在执行了t.start();后,1.非常碰巧的在t.start()后,sout前发生了一次线程调度;2.主线程状态从运行变为就绪,不再持有CPU,意味着主线程下一条语句不再执行;3.调取时,又碰巧选中子线程调度,子线程状态就绪->运行,子线程持有了CPU,所以就执行到子线程的语句了,子线程语句打印。
3)注意:一个线程调用过t.start()不能再调用+不能调成run()
a.t.start()只允许工作在“新建”状态下,调用完一次start状态就变为就绪了,再调用就会异常:
b.调用run方法,就和线程无关了,完全是在主线程下在运行代码。
public class MyThread extends Thread{
@Override
public void run() {
// 打印当前执行语句的线程的名字
System.out.println("我是 " + Thread.currentThread().getName());
}
}
t.start(); // 让 t 线程从新建 -> 就绪状态,等待被调度
// t 被调度器选中之后,才会执行
t.run(); // 在主线程中,和之前学过的 java 一样,就是一个简单的方法调用
c.ps:回顾什么时候会发生线程调度?
在多线程中,明明代码是固定的,但是会出现现象会有随机的可能性,主要原因就是调度的随机性,体现在线程的运行过程中。
4)同一个程序可以启动多个线程
我们写的无论是Thread的子类还是Runnable的实现类,只是给线程启动的“程序”。所以,同一个程序,可以启动多个线程。由于调度的随机性,导致多线程的程序,结果可能是随机的,某些语句的执行顺序可能是随机的。
package com.wy.how_to_start_thread.demo4;
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("我是"+Thread.currentThread().getName());
System.out.println("我的id是"+Thread.currentThread().getId());
}
}
public static void main(String[] args) {
//同一个“程序”可以启动多个线程
MyThread t1=new MyThread();
t1.start();
MyThread t2=new MyThread();
t2.start();
MyThread t3=new MyThread();
t3.start();
MyThread t4=new MyThread();
t4.start();
}
结果:
我是Thread-0
我的id是12
我是Thread-1
我的id是13
我是Thread-2
我的id是14
我是Thread-3
我的id是15
//结果的顺序完全随机
3.线程和方法调用栈的关系
1)每个线程都有自己独立的方法调用栈。
1)调用栈
//1
public class Add {
public static int add(int a,int b){
return a+b;
}
}
//2.
public class Main {
public static void main(String[] args) {
MyThread t=new MyThread();
t.start();
//自己在主线程也去调用ADD方法
System.out.println(Add.a