线程的相关概念
一、进程与线程
进程(progress):官方的解释是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配
与调度的基本单位,通俗来理解就是,Ctrl+Alt+Del,操作系统中所运行的一个程序。
线程(thread):线程是进程的一个执行单元,一个进程至少包含一个线程,所以可以有多个线程。
二、主线程与子线程
主线程:JVM里面会包含一个专门执行main方法的线程,就是主线程,所以主线程就是执行main方法的线程。
子线程:线程与线程直接会有着多种联系,不是独立存在的,所谓子线程可以理解为套娃,在A线程里面创建了B线程
那么B线程就是A的子线程,也可以继续在A线程里面继续创建属于A的子线程。
三、串行、并行、并发
为了更好理解表达,先假设有三个任务,A、B、C
串行:只有一条通道,A、B、C 需要一个一个的按序执行。
并行:在任务A 等待的时候,开始执行B任务,在B任务等待的时候开始执行C任务。
并发:三个任务同时开始同时执行,一般都是多核操作。
四、创建线程
1、以继承Thread类的形式创建
public class test01_ThreadCreateByExtend {
public static void main(String[] args) {
System.out.println("JVM启动main线程,执行了main方法");
MyThread myThread = new MyThread();
myThread.start();
System.out.println("main后面其他的代码");
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("调用了子线程的Run方法");
}
}
打印结果为
JVM启动main线程,执行了main方法
main后面其他的代码
调用了子线程的Run方法
注意:1.调用线程对象的**start()**方法,不意味着此线程开始运行,start方法是一种请求,请求JVM运行线程,真正执行要由
线程调度器(Scheduler)决定。
2.当线程被调用就会执行run()方法
3.线程执行顺序随机
4.多线程执行结果与代码顺序无关
案例:多线程的随机性代码演示
public class test01_RandomOfThread {
public static void main(String[] args) {
MyThread2 myThread2 = new MyThread2();
myThread2.start();
//同Thread对象里面的方法
for (int i = 1; i <= 10; i++) {
System.out.println("Main---"+i);
int time = (int) (Math.random()*1000);
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread2 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("Thread2---"+i);
int time = (int) (Math.random()*1000); //定义一个随机数
try {
Thread.sleep(time); //让线程休眠 随机时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
打印结果 每次都不相同,以此证明多线程的随机性
Main---1
Thread2---1
Main---2
Thread2---2
Thread2---3
Thread2---4
Thread2---5
Main---3
Thread2---6
Thread2---7
Main---4
Thread2---8
Main---5
Main---6
Thread2---9
Thread2---10
Main---7
Main---8
Main---9
Main---10
2、通过实现Runnable接口创建线程
注:用到了动态代理
public class test01_ThreadCreateByRunnable {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我是MyRunnable -- 通过实现Runnable接口创建");
}
}
五、线程的常用方法
1、线程的currentThread()方法
案例:打印出以下线程在对应代码位置的线程名字
public class test01_currentThread {
public static void main(String[] args) throws InterruptedException {
MyCurrentThread myCurrentThread = new MyCurrentThread();
myCurrentThread.start();
Thread.sleep(1000);
Thread tt = new Thread(myCurrentThread);
tt.start();
}
}
class MyCurrentThread extends Thread {
public MyCurrentThread() {
System.out.println("构造方法---Thread.currentThread "+Thread.currentThread().getName());
System.out.println("构造方法---this.getName "+this.getName());
}
@Override
public void run() {
System.out.println("run方法---Thread.currentThread "+Thread.currentThread().getName());
System.out.println("run方法---this.getName "+this.getName());
}
}
打印结果如下
构造方法---Thread.currentThread main
构造方法---this.getName Thread-0
run方法---Thread.currentThread Thread-0
run方法---this.getName Thread-0
run方法---Thread.currentThread Thread-1
run方法---this.getName Thread-0
解析:逆向分析–> 控制台共打印了6条语句
分辨方法:当前线程就是 谁调用了方法 就是谁
第一条:程序从main方法入口,此时只有main线程,初始化子线程myCurrentThread时,main线程会调用它的构造方法
所以,刚进入构造器的第一条线程是main线程。
第二条:同样是在构造器里面,但是 调用的是this,也就是它自己本身,它本身就是一个线程对象,所以会打印出自己。
第三条:程序继续执行,调用了myCurrentThread的start方法,线程被调用了,线程调度器就会去执行它的run方法,此时线程已 经被创建调用,所以再打印线程的名字,那就是它自己当前线程
第四条:this 同样指向它自己
第五条:程序继续执行,又创建了一个新的线程tt,但此线程与默认初始化的线程不同,把myCurrentThread作为参数传递
到新的线程里,参数所需的是一个runnable对象,Runnable对象我们提到了,需要重写它的run方法,所以此新线
程的run方法就是我们刚刚myCurrentThread的run方法,同样的执行完start方法后,新的线程被创建,那么调用run
线程就是tt新线程
第六条:涉及到新的线程里面的run方法,不过,此对象就是我们传递的myCurrentThread,所以this就是子线程1号的名字。
2、线程的isAlive方法
作用:isAlive方法可以判断当前线程是否处于活动状态
案例:查看线程是否处于活动状态
public class test01_isAliveMethod {
public static void main(String[] args) {
ThreadIsAlive thread = new ThreadIsAlive();
System.out.println("Main方法里面 is Alive "+thread.isAlive());
thread.start();
System.out.println("Main 后面 is Alive "+thread.isAlive());
}
}
class ThreadIsAlive extends Thread{
@Override
public void run() {
System.out.println("run方法里面 is Alive "+this.isAlive());
}
}
打印结果
Main方法里面 is Alive false
Main 后面 is Alive true
run方法里面 is Alive true
3、线程的sleep休眠
作用:调用Thread.sleep(ms)可以使当前的线程休眠一段时间
案例:计时器
public class test01_sleepThread {
public static void main(String[] args) throws InterruptedException {
int count = 5;
while (true){
if(count<=0){
break;
}
System.out.println("倒计时:"+count);
Thread.sleep(1000);
count--;
}
System.out.println("结束!");
}
}
4、线程的getId方法
作用:能够获取线程的唯一标识
5、线程的yield方法
作用:进行线程让步
案例:对子线程进行线程让步,进行与main线程的耗时对比
public class test01_yieldThread {
public static void main(String[] args) {
YieldThread thread = new YieldThread();
thread.start();
long begin = System.currentTimeMillis();
int sum = 0;
for (int i = 0; i < 1000000; i++) {
sum+=i;
}
long end = System.currentTimeMillis();
System.out.println("main线程所有时间 "+(end-begin));
}
}
class YieldThread extends Thread {
@Override
public void run() {
long begin = System.currentTimeMillis();
int sum=0;
for (int i = 0; i < 1000000; i++) {
sum+=i;
Thread.yield();
}
long end = System.currentTimeMillis();
System.out.println("子线程所用时间 "+(end-begin));
}
}
打印结果
main线程所有时间 4
子线程所用时间 712
6、线程的setPriority方法
作用:设置线程的优先级,在日常开发中很少使用,范围1~10
注意:
- 线程优先级本质上是给线程调度器一个提示信息,可以决定先执行那些线程,但不保证优先级高的一定先执行
- 优先级设置不当可能会导致线程饥饿
- 线程优先级具有继承性
7、线程的interrupt方法
作用:可以标识一个线程是否要被打断,并不是真正意义上的打断,需要配合isInterruptted方法使用
案例:配合interruptted方法,打断子线程的输出
public class test01_interrputThread {
public static void main(String[] args) {
InterruptThread thread = new InterruptThread();
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("main线程 "+i);
}
thread.interrupt(); //标识打断子线程
}
}
class InterruptThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(this.isInterrupted()){ //判断是否被打断,为真 就直接跳出循环
break;
}
System.out.println("子线程线程 "+i);
}
}
}
打印结果
main线程 96
main线程 97
main线程 98
main线程 99
子线程线程 49
8、线程的setDaemon方法
作用:设置线程为守护线程
注意:Java线程分为 用户线程和守护线程,GC就是一个守护线程,守护线程不可单独使用,JVM中只有守护线程时,会自动退出
在设置为main线程的守护线程时,main线程结束,其对应的守护线程也会被销毁
设置守护线程的代码,一定要在start()方法之前,否则不生效