对于一个java程序来说,当开始执行后,会先启动JVM,而JVM就是一个进程。
JVM再启动一个主线程调用main方法,同时再启动一个垃圾回收线程负责看护,回收垃圾。故一个java程序起码有两个线程并发。
举个栗子:
进程可看作是一个公司。
线程可看作是公司里的一名员工。
注意:
假设启动10个线程,就会开辟10个栈空间,每个栈之间互不干扰,各自执行各自的,这就是多线程并发。
学校食堂可以看作是一个进程,
食堂里每一个窗口可以看作是一个线程,
我在A窗口打饭,你可以在B窗口打饭,你不需要等我,我也不需要等你。
故多线程并发可以提高效率。
Java中之所以有多线程机制,目的就是为了提高程序的处理效率。
注意:
使用了多线程机制后,main方法结束,有可能程序也不会结束。
main方法结束只代表主线程结束了,主栈空了,其他的栈(线程)可能还在压栈弹栈。
对于单核的cpu来说,可以做到真正多线程并发吗?
什么是真正的多线程并发?
t1线程执行t1的,t2执行t2的,t1不会影响到t2,t2也不会影响t1。
这就是真正的多线程并发。
对于单核的cpu来说只有一个大脑,无法做到真正的多线程并发,但可以给人一种"多线程并发的感觉"。
在某一个时间点上,单核的cpu只能执行一个线程,但是由于cpu执行速度极快,通过频繁切换不同的线程,给人一种同时处理多个线程的感觉。
就好比你在玩炉石,同时后台用网易云播放着"蜜雪冰城甜蜜蜜"。
线程A:炉石
线程B:网易云"蜜雪冰城甜蜜蜜"
线程A和线程B频繁切换运行,你就感觉游戏一直在运行,音乐一直在播放。
以下程序中有几个线程?
/**
* @Author: TSCCG
* @Date: 2021/06/18 17:41
*/
public class ThreadDemo01 {
public static void main(String[] args) {
System.out.println("main begin");
t1();
System.out.println("main over");
}
private static void t1() {
System.out.println("t1 begin");
t2();
System.out.println("t1 over");
}
private static void t2() {
System.out.println("t2 begin");
t3();
System.out.println("t2 over");
}
private static void t3() {
System.out.println("t3执行!");
}
}
答:除垃圾回收外,只有一个main主线程,主栈
理由:没有启动分支栈,没有启动分支线程,故只有一个主线程,一个栈
结果:
main begin
t1 begin
t2 begin
t3执行!
t2 over
t1 over
main over
若要开启一条新的线程,必须要用到start()方法和run方法
start方法:
run()方法:
实现线程的第一种方法:
写一个继承Thread类的类,重写run方法。
使用时创建一个线程对象,然后调用start()方法。
/**
* @Author: TSCCG
* @Date: 2021/06/18 20:02
*/
public class ThreadDemo02 {
//main方法,属于主线程,运行在主栈中
public static void main(String[] args) {
//创建一个分支线程对象
MyThread01 myThread = new MyThread01();
//启动线程,启动完后立即结束start方法
myThread.start();
//此处如果没有调用start方法,直接调用run方法,程序仍然只有一个线程
//因为没有开启新的线程,相当于是调用了一个普通方法
//myThread.run();
for (int i = 0; i < 10; i++) {
System.out.println("主线程---->" + i);
}
}
}
class MyThread01 extends Thread{
/**
* 此处重写的run方法相当于主线程里的main
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("分支线程---->" + i);
}
}
}
结果:
主线程---->0
分支线程---->0
分支线程---->1
分支线程---->2
主线程---->1
分支线程---->3
分支线程---->4
主线程---->2
分支线程---->5
主线程---->3
主线程---->4
分支线程---->6
分支线程---->7
分支线程---->8
分支线程---->9
主线程---->5
主线程---->6
主线程---->7
主线程---->8
主线程---->9
创建线程的第二种方法:
写一个实现Runnable接口的类,叫可执行类
使用时将该类的实例对象作为参数传入Thread类里,转换成线程对象,然后通过该线程对象调用start()方法
/**
* @Author: TSCCG
* @Date: 2021/06/19 09:29
*/
public class ThreadDemo03 {
public static void main(String[] args) {
//创建一个可运行类
MyRunnable r = new MyRunnable();
//将可运行类的对象作为参数传入Thread类的构造方法里,转换成线程对象
Thread t = new Thread(r);//也可以这样写:Thread t2 = new Thread(new MyRunnable());
//开辟一个分支栈,开启分支线程
t3.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("分支线程---->" + i);
}
}
}
也可以采用匿名内部类方式
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("分支线程---->" + i);
}
}
});
t3.start();
一个线程的周期一共分为五个状态:
新建状态----线程对象刚被new出来
就绪状态----调用start方法后,开辟了栈空间,可以抢夺CPU时间片(执行权)
运行状态----抢夺到CPU时间片后的线程,开始执行各自的程序,主线程执行main方法,分支线程执行run方法。当执行完时间片的时间后,会重新回到就绪状态
阻塞状态----当遇到sleep、Scanner等事件时,线程就会进入阻塞状态,放弃占有的CPU时间片,当结束阻塞状态时,再次回到就绪状态
死亡状态----当线程的main或run方法执行完后,就会进入死亡状态
import java.util.Scanner;
/**
* @Author: TSCCG
* @Date: 2021/06/19 09:29
*/
public class ThreadDemo03 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//创建线程对象
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("分支线程---->" + i);
}
}
});
//开启分支线程
t3.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程---->" + i);
//放置一个阻塞事件,当i == 5时,主线程阻塞,分支线程继续执行,
//当输入一个整数时,阻塞事件结束,主线程继续运行
if(i == 5){
System.out.println("遭遇阻塞事件");
int j = sc.nextInt();
System.out.println("阻塞事件结束");
}
}
}
}
结果:
主线程---->0
主线程---->1
主线程---->2
主线程---->3
主线程---->4
分支线程---->0
分支线程---->1
分支线程---->2
分支线程---->3
分支线程---->4
分支线程---->5
主线程---->5
遭遇阻塞事件
分支线程---->6
分支线程---->7
分支线程---->8
分支线程---->9
3
阻塞事件结束
主线程---->6
主线程---->7
主线程---->8
主线程---->9
静态方法,通过Thread来调用
Thread t = Thread.currentThread();
String name = 线程对象.getName();
默认情况下,线程的名字默认为:
Thread-0
Thread-1
Thread-2
线程对象.setName("线程名字");
案例:
/**
* @Author: TSCCG
* @Date: 2021/06/19 10:27
*/
public class ThreadDemo04 {
public static void main(String[] args) {
//创建线程对象t1
MineThread t1 = new MineThread();
//设置线程t1的名字
t1.setName("t1");
//开启线程t1
t1.start();
//创建线程对象t2
MineThread t2 = new MineThread();
t2.setName("t2");
t2.start();
for (int i = 0; i < 10; i++) {
//获取当前线程对象
Thread t = Thread.currentThread();
//获取当前线程对象
System.out.println(t.getName() + "---->" + i);
}
}
}
class MineThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
//获取当前线程对象
Thread t = Thread.currentThread();
//获取当前线程对象
System.out.println(t.getName() + "---->" + i);
}
}
}
结果:
main---->0
main---->1
main---->2
main---->3
t2---->0
t1---->0
t1---->1
t2---->1
t2---->2
main---->4
t2---->3
t1---->2
t2---->4
main---->5
t2---->5
t1---->3
t2---->6
main---->6
t2---->7
t1---->4
t2---->8
t2---->9
main---->7
t1---->5
t1---->6
main---->8
t1---->7
main---->9
t1---->8
t1---->9
static void sleep(long millis)
1.静态方法:Thread.sleep(1000);
2.单位是1毫秒
3.作用:让当前线程进入休眠,进入"阻塞状态",放弃占有CUP时间片,让给其他线程使用
注意:
Thread.sleep(1000);出现在哪个线程,哪个线程就会进入休眠
例:
/**
* @Author: TSCCG
* @Date: 2021/06/19 10:55
*/
public class ThreadDemo05 {
public static void main(String[] args) {
System.out.println("让子弹飞一会儿");
try {
Thread.sleep(1000 * 5);//让主线程休眠5s
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);//每次休眠1s
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("砰!---->" + i);
}
}
}
结果:
让子弹飞一会儿
砰!---->0
砰!---->1
砰!---->2
砰!---->3
砰!---->4
砰!---->5
砰!---->6
砰!---->7
砰!---->8
砰!---->9
判断以下程序中t1线程是否会休眠2s?
/**
* @Author: TSCCG
* @Date: 2021/06/27 15:43
* sleep()面试题
*/
public class ThreadDemo06 {
public static void main(String[] args) {
Thread t1 = new MineThread02();
t1.start();
t1.setName("t1");
try {
//这里使用线程对象t1调用sleep()方法
t1.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("HelloWorld!");
}
}
class MineThread02 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Thread t = Thread.currentThread();
System.out.println(t.getName() + "---->" + i);
}
}
}
结果:
t1---->0
t1---->1
t1---->2
t1---->3
t1---->4
t1---->5
t1---->6
t1---->7
t1---->8
t1---->9
HelloWorld!
从结果可见,通过线程对象t1调用sleep()方法后,t1线程并没有休眠,而是主线程休眠2s。
为什么呢?
这是因为sleep()是静态方法,不应该通过类对象来调用,只能通过类‘Thread‘直接访问。
t1.sleep(2000);这句代码实际执行的是Thread.sleep(2000);
通过调用interrupt()方法可以中断程序的休眠
/**
* @Author: TSCCG
* @Date: 2021/06/27 11:23
*/
public class ThreadDemo07 {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable02());
t.setName("t");
t.start();
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//在主线程休眠5s后,将t线程唤醒
t.interrupt();
}
}
class MyRunnable02 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "----> begin");
//在run()方法中,只能通过try...catch处理异常,而不能用throws将异常抛出
//因为run()方法在父类中没有抛出任何异常,而子类不能抛出更多的异常
try {
//使分支线程休眠1年
Thread.sleep(1000 * 60 * 60 * 24 * 365);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "----> end");
}
}
结果:
在打印"t----> begin" 五秒后打印线程休眠中断异常信息,然后打印"t----> end"
t----> begin
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at MyRunnable02.run(ThreadDemo07.java:28)
at java.lang.Thread.run(Thread.java:745)
t----> end
原文:https://www.cnblogs.com/TSCCG/p/14940253.html