目录
第一个线程程序
创建线程
四种创建方式
继承Thread类
实现Runnable接口
Thread的常用方法
构造方法
Thread的核心属性
线程的启动
获取当前正在执行的线程对象
线程的中断
等待其他线程
休眠当前线程
多线程的作用
第一个线程程序
- 我们知道一个类的启动是一个进程
- 一个类中的主方法是一个进程的主线程,所有的调用都是从主方法开始的,所有的任务都是从主方法中进行
- 用thread类来创建线程,java.lang.thread,都是通过它来启动一个新的线程
package thread;
import java.util.Random;
public class FirstThreadDemo {
private static class MyThread extends Thread{
@Override
public void run() {
Random random=new Random();
while (true){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
MyThread m1=new MyThread();
MyThread m2=new MyThread();
MyThread m3=new MyThread();
//启动三个线程
m1.start();
m2.start();
m3.start();
Random random=new Random();
while (true){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
其线程的执行顺序
- 线程的执行顺序不是像单线程那样按照顺序执行的,其几个线程是“同时”进行的,这里的同时表示的是在宏观上是并行的,在微观上是并发执行
思考题
jconsole 查看当前JVM的内部线程情况
创建线程
四种创建方式
- 继承Thread类,覆写run方法(线程的核心工资任务方法)
- 覆写Runnable接口,覆写run方法
- 覆写Callable接口,覆写call方法
- 使用线程池创建线程
继承Thread类
- 一个子类继承Thread类
- 覆写run方法
- 产生这个子类对象,然后调用start方法启动线程
可以看到相同的代码输出的顺序不一样,因为start启动线程,是由JVM产生操作系统的线程并启动,到底什么时候真正启动,我们是不可见的,也没法控制,而且线程的执行也是并行的
匿名内部类写法
Lambda写法
不能用,因为Thread不是函数式接口
实现Runnable接口
- 先实现Runnable接口
- 覆写run方法
- 先创建一个子类对象,然后创建一个Thread对象,接收这个子类对象
推荐第二种方法,因为实现Runnable接口更加灵活,子类还能实现其他的接口,继承别的类
匿名内部类写法
Lambda写法
- Lambda只能用来实现函数式接口,只有一个抽象方法的接口
Thread的常用方法
构造方法
Thread的核心属性
- 每个线程都有自己唯一的ID
- 优先级越高的线程是越有可能被CPU优先执行,我们JAVA只是建议优先级高的线程被执行,到底执行不执行,由操作系统说的算
- JVM会在一个进程所有的非后台进程结束后,才会结束运行
- 是否存活,就是为run方法是否运行结束
线程的启动
之前我们已经看到了如何通过覆写
run
方法创建一个线程对象,但线程对象被创建出来并不意味着线程 就开始运行了。
- 覆写 run 方法是提供给线程要做的事情的指令清单
- 线程对象可以认为是把 李四、王五叫过来了
- 而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了
- 调用 start 方法, 才真的在操作系统的底层创建出一个线程.
- 无论是继承Thread类还是实现Runnable接口,最终启动还是使用Thread的start方法,Thread类就是JVM用来描述管理线程的类,每个线程都对应唯一一个Thread对象
获取当前正在执行的线程对象
线程的中断
定义:中断一个正在运行的线程,(run方法还没有执行结束),普通线程会在run方法执行结束后自动停止
我们的中断其实就是更改线程的状态,想让线程终止,只有run方法执行完毕,就自然终止了
两种中断方式
- 通过共享变量进行中断
- 使用Thread.interrupted()静态方法
共享变量进行中断
package Lambda;
public class ThreadInterrupted {
private static class MyThread implements Runnable{
volatile boolean isQuit=false;//共享变量
@Override
public void run() {
while (!isQuit){
System.out.println(Thread.currentThread().getName()+"我正在工作,别打扰我");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"被中断了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread m1=new MyThread();
Thread t1=new Thread(m1,"帅哥线程");
System.out.println("帅哥开始工作");
t1.start();
Thread.sleep(3000);
System.out.println("帅哥工作结束了");
m1.isQuit=true;
}
}
- 因为线程的并发执行,可能每次的执行结果都不一样
- Thread的静态方法在那个线程调用,就生效在那个线程
- 共享变量用volatile,volatile修饰的作用后面讲
使用interrupted静态方法中断或者Thread对象的成员方法isInterrupted
- 原理:当前Thread类中特别设置了一个属性,当前线程是否被中断的属性,如果为true,表示当前是中断状态,为false说明不是中断状态,调用x线程对象.interrupt方法就会讲线程对象的状态设置为中断状态(true)
线程收到interrupt内置中断通知的两种处理方法
- 当线程调用sleep/wait/join等方法处于阻塞状态的时候,收到thread.interrupt(),就会抛出一个中断异常,InterruptedException,当抛出这个异常的时候(无论是使用那种判断方式),当前线程的中断状态会被清除(也就是Thread类中那个特别设置的中断属性)
- 当没有调用以上三种方法,处在正常运行状态,收到中断通知thread.interrupt(),Thread.interrupted会判断当前线程是否被中断,若为true,清除中断标志变为false,线程对象.isInetrrupted()判断线程对象是否为中断状态,若状态为true,不会清除中断标志,保持为true
isinterrupetd成员方法和interrupted类方法的区别(当不是阻塞状态)
当子线程处于阻塞状态对于中断信号的处理
package Lambda;
public class ThreadInterruptedByMethod {
private static class MyThread implements Runnable{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"我正在工作");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("我休息一下");
break;
}
}
System.out.println(Thread.currentThread().getName()+"到点了,我要下班了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread m1=new MyThread();
Thread t1=new Thread(m1,"帅哥");
System.out.println("帅哥准备开始工作");
t1.start();
Thread.sleep(5*1000);
t1.interrupt();//中断t1线程
}
}
等待其他线程
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转 账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。
- 我们用线程对象.join()成员方法来实现,在哪个线程调用别的线程对象的join方法,意思就是这个线程要等待另一个线程执行完毕再能继续执行本线程
- 第一个就是死等,痴汉属性
- 第二个是有理性的等,最多等你多少毫秒,如果执行完最后,执行不完,我也不等你了
package Lambda;
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"正在学习JavaSE阶段");
try {
Thread.sleep(1000);//表示此进程休息一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"刘颂成");
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"正在学习数据结构阶段");
try {
Thread.sleep(1000);//表示此进程休息一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"进化的刘颂成");
System.out.println("先学习JavaSE");
t1.start();
t1.join();//此时走到这里,t1线程已经执行,main主线程必须先执行完t1子线程,再去执行本线程或者其他线程
t2.start();
t2.join();此时走到这里,t2线程已经执行,main主线程必须先执行完t2子线程,再去执行本线程或者其他线程
System.out.println("开始学习JavaEE");
}
}
去除两个join的执行过程
休眠当前线程
- 该方法通过Thread这个类调用,意思就是在哪个线程的内部调用,那么就休眠哪个线程
多线程的作用
比较一下多线程和顺序执行的速度差异,比如20个亿数字的连续累加
package Lambda;
public class ThreadNB {
private static final long count=10_0000_0000;
public static void main(String[] args) throws InterruptedException {
serial();
concurrent();
}
public static void serial(){
long start=System.nanoTime();
long a=0;
for (int i = 0; i < count; i++) {
a++;
}
long b=0;
for (int i = 0; i < count; i++) {
b++;
}
long end=System.nanoTime();
double allTime=(end-start)*1.0/1000/1000;
System.out.println("串行执行所用的时间"+allTime+"ms");
}
public static void concurrent() throws InterruptedException {
//并行实现20亿的累加
long start=System.nanoTime();
Thread thread1=new Thread(()->{
long a=0;
for (int i = 0; i < count; i++) {
a++;
}
});
thread1.start();//子线程进行十亿次累加
//主线程也进行10亿次累加
long b=0;
for (int i = 0; i < count; i++) {
b++;
}
// 等待子线程执行结束,主线程和子线程的加法操作都完成
// 等待子线程thread执行结束才能执行下面代码
thread1.join();//限制子线程执行完毕,才能运行下面的代码
long end=System.nanoTime();
double allTime=(end-start)*1.0/1000/1000;
System.out.println("并行耗费的时间为"+allTime+"ms");
}
}
- 理论上并发的执行速度应该是顺序执行的一倍
- 多线程的最大应用场景就是把一个大任务拆分为多个子任务(交给子线程),多个子线程并发执行,提高系统的处理效率,比如12306系统就是一个多线程程序,我们每个人其实都是一个线程,我们多个人可以同时登录系统买票,付款操作是一个非常耗时的操作,如果不是多线程,每个人买票就得像排队买票一样,依次进行,非常慢,有多线程(就可以趁着比如调整付款页面的时间去处理别人买票的操作)
- 我们知道一个类的启动是一个进程
- 一个类中的主方法是一个进程的主线程,所有的调用都是从主方法开始的,所有的任务都是从主方法中进行
- 用thread类来创建线程,java.lang.thread,都是通过它来启动一个新的线程
package thread;
import java.util.Random;
public class FirstThreadDemo {
private static class MyThread extends Thread{
@Override
public void run() {
Random random=new Random();
while (true){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
MyThread m1=new MyThread();
MyThread m2=new MyThread();
MyThread m3=new MyThread();
//启动三个线程
m1.start();
m2.start();
m3.start();
Random random=new Random();
while (true){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
其线程的执行顺序
- 线程的执行顺序不是像单线程那样按照顺序执行的,其几个线程是“同时”进行的,这里的同时表示的是在宏观上是并行的,在微观上是并发执行
思考题
jconsole 查看当前JVM的内部线程情况
四种创建方式
- 继承Thread类,覆写run方法(线程的核心工资任务方法)
- 覆写Runnable接口,覆写run方法
- 覆写Callable接口,覆写call方法
- 使用线程池创建线程
继承Thread类
- 一个子类继承Thread类
- 覆写run方法
- 产生这个子类对象,然后调用start方法启动线程
可以看到相同的代码输出的顺序不一样,因为start启动线程,是由JVM产生操作系统的线程并启动,到底什么时候真正启动,我们是不可见的,也没法控制,而且线程的执行也是并行的
匿名内部类写法
Lambda写法
不能用,因为Thread不是函数式接口
实现Runnable接口
- 先实现Runnable接口
- 覆写run方法
- 先创建一个子类对象,然后创建一个Thread对象,接收这个子类对象
推荐第二种方法,因为实现Runnable接口更加灵活,子类还能实现其他的接口,继承别的类
匿名内部类写法
Lambda写法
- Lambda只能用来实现函数式接口,只有一个抽象方法的接口
Thread的常用方法
构造方法
Thread的核心属性
- 每个线程都有自己唯一的ID
- 优先级越高的线程是越有可能被CPU优先执行,我们JAVA只是建议优先级高的线程被执行,到底执行不执行,由操作系统说的算
- JVM会在一个进程所有的非后台进程结束后,才会结束运行
- 是否存活,就是为run方法是否运行结束
线程的启动
之前我们已经看到了如何通过覆写
run
方法创建一个线程对象,但线程对象被创建出来并不意味着线程 就开始运行了。
- 覆写 run 方法是提供给线程要做的事情的指令清单
- 线程对象可以认为是把 李四、王五叫过来了
- 而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了
- 调用 start 方法, 才真的在操作系统的底层创建出一个线程.
- 无论是继承Thread类还是实现Runnable接口,最终启动还是使用Thread的start方法,Thread类就是JVM用来描述管理线程的类,每个线程都对应唯一一个Thread对象
获取当前正在执行的线程对象
线程的中断
定义:中断一个正在运行的线程,(run方法还没有执行结束),普通线程会在run方法执行结束后自动停止
我们的中断其实就是更改线程的状态,想让线程终止,只有run方法执行完毕,就自然终止了
两种中断方式
- 通过共享变量进行中断
- 使用Thread.interrupted()静态方法
共享变量进行中断
package Lambda;
public class ThreadInterrupted {
private static class MyThread implements Runnable{
volatile boolean isQuit=false;//共享变量
@Override
public void run() {
while (!isQuit){
System.out.println(Thread.currentThread().getName()+"我正在工作,别打扰我");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"被中断了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread m1=new MyThread();
Thread t1=new Thread(m1,"帅哥线程");
System.out.println("帅哥开始工作");
t1.start();
Thread.sleep(3000);
System.out.println("帅哥工作结束了");
m1.isQuit=true;
}
}
- 因为线程的并发执行,可能每次的执行结果都不一样
- Thread的静态方法在那个线程调用,就生效在那个线程
- 共享变量用volatile,volatile修饰的作用后面讲
使用interrupted静态方法中断或者Thread对象的成员方法isInterrupted
- 原理:当前Thread类中特别设置了一个属性,当前线程是否被中断的属性,如果为true,表示当前是中断状态,为false说明不是中断状态,调用x线程对象.interrupt方法就会讲线程对象的状态设置为中断状态(true)
线程收到interrupt内置中断通知的两种处理方法
- 当线程调用sleep/wait/join等方法处于阻塞状态的时候,收到thread.interrupt(),就会抛出一个中断异常,InterruptedException,当抛出这个异常的时候(无论是使用那种判断方式),当前线程的中断状态会被清除(也就是Thread类中那个特别设置的中断属性)
- 当没有调用以上三种方法,处在正常运行状态,收到中断通知thread.interrupt(),Thread.interrupted会判断当前线程是否被中断,若为true,清除中断标志变为false,线程对象.isInetrrupted()判断线程对象是否为中断状态,若状态为true,不会清除中断标志,保持为true
isinterrupetd成员方法和interrupted类方法的区别(当不是阻塞状态)
当子线程处于阻塞状态对于中断信号的处理
package Lambda;
public class ThreadInterruptedByMethod {
private static class MyThread implements Runnable{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"我正在工作");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("我休息一下");
break;
}
}
System.out.println(Thread.currentThread().getName()+"到点了,我要下班了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread m1=new MyThread();
Thread t1=new Thread(m1,"帅哥");
System.out.println("帅哥准备开始工作");
t1.start();
Thread.sleep(5*1000);
t1.interrupt();//中断t1线程
}
}
等待其他线程
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转 账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。
- 我们用线程对象.join()成员方法来实现,在哪个线程调用别的线程对象的join方法,意思就是这个线程要等待另一个线程执行完毕再能继续执行本线程
- 第一个就是死等,痴汉属性
- 第二个是有理性的等,最多等你多少毫秒,如果执行完最后,执行不完,我也不等你了
package Lambda;
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"正在学习JavaSE阶段");
try {
Thread.sleep(1000);//表示此进程休息一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"刘颂成");
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"正在学习数据结构阶段");
try {
Thread.sleep(1000);//表示此进程休息一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"进化的刘颂成");
System.out.println("先学习JavaSE");
t1.start();
t1.join();//此时走到这里,t1线程已经执行,main主线程必须先执行完t1子线程,再去执行本线程或者其他线程
t2.start();
t2.join();此时走到这里,t2线程已经执行,main主线程必须先执行完t2子线程,再去执行本线程或者其他线程
System.out.println("开始学习JavaEE");
}
}
去除两个join的执行过程
休眠当前线程
- 该方法通过Thread这个类调用,意思就是在哪个线程的内部调用,那么就休眠哪个线程
多线程的作用
比较一下多线程和顺序执行的速度差异,比如20个亿数字的连续累加
package Lambda;
public class ThreadNB {
private static final long count=10_0000_0000;
public static void main(String[] args) throws InterruptedException {
serial();
concurrent();
}
public static void serial(){
long start=System.nanoTime();
long a=0;
for (int i = 0; i < count; i++) {
a++;
}
long b=0;
for (int i = 0; i < count; i++) {
b++;
}
long end=System.nanoTime();
double allTime=(end-start)*1.0/1000/1000;
System.out.println("串行执行所用的时间"+allTime+"ms");
}
public static void concurrent() throws InterruptedException {
//并行实现20亿的累加
long start=System.nanoTime();
Thread thread1=new Thread(()->{
long a=0;
for (int i = 0; i < count; i++) {
a++;
}
});
thread1.start();//子线程进行十亿次累加
//主线程也进行10亿次累加
long b=0;
for (int i = 0; i < count; i++) {
b++;
}
// 等待子线程执行结束,主线程和子线程的加法操作都完成
// 等待子线程thread执行结束才能执行下面代码
thread1.join();//限制子线程执行完毕,才能运行下面的代码
long end=System.nanoTime();
double allTime=(end-start)*1.0/1000/1000;
System.out.println("并行耗费的时间为"+allTime+"ms");
}
}
- 理论上并发的执行速度应该是顺序执行的一倍
- 多线程的最大应用场景就是把一个大任务拆分为多个子任务(交给子线程),多个子线程并发执行,提高系统的处理效率,比如12306系统就是一个多线程程序,我们每个人其实都是一个线程,我们多个人可以同时登录系统买票,付款操作是一个非常耗时的操作,如果不是多线程,每个人买票就得像排队买票一样,依次进行,非常慢,有多线程(就可以趁着比如调整付款页面的时间去处理别人买票的操作)
构造方法
Thread的核心属性
- 每个线程都有自己唯一的ID
- 优先级越高的线程是越有可能被CPU优先执行,我们JAVA只是建议优先级高的线程被执行,到底执行不执行,由操作系统说的算
- JVM会在一个进程所有的非后台进程结束后,才会结束运行
- 是否存活,就是为run方法是否运行结束
线程的启动
之前我们已经看到了如何通过覆写
run
方法创建一个线程对象,但线程对象被创建出来并不意味着线程 就开始运行了。
- 覆写 run 方法是提供给线程要做的事情的指令清单
- 线程对象可以认为是把 李四、王五叫过来了
- 而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了
- 调用 start 方法, 才真的在操作系统的底层创建出一个线程.
- 无论是继承Thread类还是实现Runnable接口,最终启动还是使用Thread的start方法,Thread类就是JVM用来描述管理线程的类,每个线程都对应唯一一个Thread对象
获取当前正在执行的线程对象
线程的中断
定义:中断一个正在运行的线程,(run方法还没有执行结束),普通线程会在run方法执行结束后自动停止
我们的中断其实就是更改线程的状态,想让线程终止,只有run方法执行完毕,就自然终止了 两种中断方式- 通过共享变量进行中断
- 使用Thread.interrupted()静态方法
共享变量进行中断
package Lambda;
public class ThreadInterrupted {
private static class MyThread implements Runnable{
volatile boolean isQuit=false;//共享变量
@Override
public void run() {
while (!isQuit){
System.out.println(Thread.currentThread().getName()+"我正在工作,别打扰我");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"被中断了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread m1=new MyThread();
Thread t1=new Thread(m1,"帅哥线程");
System.out.println("帅哥开始工作");
t1.start();
Thread.sleep(3000);
System.out.println("帅哥工作结束了");
m1.isQuit=true;
}
}
- 因为线程的并发执行,可能每次的执行结果都不一样
- Thread的静态方法在那个线程调用,就生效在那个线程
- 共享变量用volatile,volatile修饰的作用后面讲
使用interrupted静态方法中断或者Thread对象的成员方法isInterrupted
- 原理:当前Thread类中特别设置了一个属性,当前线程是否被中断的属性,如果为true,表示当前是中断状态,为false说明不是中断状态,调用x线程对象.interrupt方法就会讲线程对象的状态设置为中断状态(true)
线程收到interrupt内置中断通知的两种处理方法
- 当线程调用sleep/wait/join等方法处于阻塞状态的时候,收到thread.interrupt(),就会抛出一个中断异常,InterruptedException,当抛出这个异常的时候(无论是使用那种判断方式),当前线程的中断状态会被清除(也就是Thread类中那个特别设置的中断属性)
- 当没有调用以上三种方法,处在正常运行状态,收到中断通知thread.interrupt(),Thread.interrupted会判断当前线程是否被中断,若为true,清除中断标志变为false,线程对象.isInetrrupted()判断线程对象是否为中断状态,若状态为true,不会清除中断标志,保持为true
isinterrupetd成员方法和interrupted类方法的区别(当不是阻塞状态)
当子线程处于阻塞状态对于中断信号的处理
package Lambda;
public class ThreadInterruptedByMethod {
private static class MyThread implements Runnable{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"我正在工作");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("我休息一下");
break;
}
}
System.out.println(Thread.currentThread().getName()+"到点了,我要下班了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread m1=new MyThread();
Thread t1=new Thread(m1,"帅哥");
System.out.println("帅哥准备开始工作");
t1.start();
Thread.sleep(5*1000);
t1.interrupt();//中断t1线程
}
}
等待其他线程
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转 账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。
- 我们用线程对象.join()成员方法来实现,在哪个线程调用别的线程对象的join方法,意思就是这个线程要等待另一个线程执行完毕再能继续执行本线程
- 第一个就是死等,痴汉属性
- 第二个是有理性的等,最多等你多少毫秒,如果执行完最后,执行不完,我也不等你了
package Lambda;
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"正在学习JavaSE阶段");
try {
Thread.sleep(1000);//表示此进程休息一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"刘颂成");
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"正在学习数据结构阶段");
try {
Thread.sleep(1000);//表示此进程休息一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"进化的刘颂成");
System.out.println("先学习JavaSE");
t1.start();
t1.join();//此时走到这里,t1线程已经执行,main主线程必须先执行完t1子线程,再去执行本线程或者其他线程
t2.start();
t2.join();此时走到这里,t2线程已经执行,main主线程必须先执行完t2子线程,再去执行本线程或者其他线程
System.out.println("开始学习JavaEE");
}
}
去除两个join的执行过程
休眠当前线程
- 该方法通过Thread这个类调用,意思就是在哪个线程的内部调用,那么就休眠哪个线程
比较一下多线程和顺序执行的速度差异,比如20个亿数字的连续累加
package Lambda; public class ThreadNB { private static final long count=10_0000_0000; public static void main(String[] args) throws InterruptedException { serial(); concurrent(); } public static void serial(){ long start=System.nanoTime(); long a=0; for (int i = 0; i < count; i++) { a++; } long b=0; for (int i = 0; i < count; i++) { b++; } long end=System.nanoTime(); double allTime=(end-start)*1.0/1000/1000; System.out.println("串行执行所用的时间"+allTime+"ms"); } public static void concurrent() throws InterruptedException { //并行实现20亿的累加 long start=System.nanoTime(); Thread thread1=new Thread(()->{ long a=0; for (int i = 0; i < count; i++) { a++; } }); thread1.start();//子线程进行十亿次累加 //主线程也进行10亿次累加 long b=0; for (int i = 0; i < count; i++) { b++; } // 等待子线程执行结束,主线程和子线程的加法操作都完成 // 等待子线程thread执行结束才能执行下面代码 thread1.join();//限制子线程执行完毕,才能运行下面的代码 long end=System.nanoTime(); double allTime=(end-start)*1.0/1000/1000; System.out.println("并行耗费的时间为"+allTime+"ms"); } }
- 理论上并发的执行速度应该是顺序执行的一倍
- 多线程的最大应用场景就是把一个大任务拆分为多个子任务(交给子线程),多个子线程并发执行,提高系统的处理效率,比如12306系统就是一个多线程程序,我们每个人其实都是一个线程,我们多个人可以同时登录系统买票,付款操作是一个非常耗时的操作,如果不是多线程,每个人买票就得像排队买票一样,依次进行,非常慢,有多线程(就可以趁着比如调整付款页面的时间去处理别人买票的操作)



