线程是什么
Java中一个线程就是一个 “执行流”. 每个线程有自己的任务. 多个线程之间 “同时” 各自完成着自己的任务.
线程和进程的区别
- 进程包含线程,一个进程里可以有一个线程,也可以有多个线程.
- 进程和线程都是为了处理并发编程这样的场景.但是进程有问题,频繁的穿件和释放的时候效率低,相比之下,线程更轻量,创建和释放效率更高.(轻量的原因是减少了申请释放资源的过程)
- 操作系统创建进程,要给进程分配资源,进程是操作系统分配资源的进本单位.而操作系统创建线程,是要在CPU上调度执行,线程是操作系统调度执行的基本单位.
- 进程具有独立性.每个进程有各自的虚拟地址空间,一个进程挂了,不会影响到其他进程.而同一个进程中的多个线程则是共用的一个内存空间,一个线程挂了,就有可能影响到其他线程,甚至导致整个进程奔溃.
Java中创建线程的方式
多线程程序和普通程序的区别:
- 每个线程都是一个独立的执行流
- 多个线程之间是 “并发” 执行的
先来感受一个多线程程序
importjava.util.Random;publicclassThreadDemo{privatestaticclassMyThreadextendsThread{@Overridepublicvoidrun(){//用户产生随机数Random random =newRandom();while(true){// 打印线程名称 Thread.currentThread()用来获取当前线程实例System.out.println(Thread.currentThread().getName());try{// 随机停止运行 0-9 秒 Thread.sleep();让程序进入阻塞状态.参数为时间,单位为毫秒Thread.sleep(random.nextInt(10));}catch(InterruptedException e){
e.printStackTrace();}}}}publicstaticvoidmain(String[] args){MyThread t1 =newMyThread();MyThread t2 =newMyThread();MyThread t3 =newMyThread();
t1.start();
t2.start();
t3.start();Random random =newRandom();while(true){// 打印线程名称System.out.println(Thread.currentThread().getName());try{//让main线程随机休眠.Thread.sleep(random.nextInt(10));}catch(InterruptedException e){// 随机停止运行 0-9 秒
e.printStackTrace();}}}}
1.继承Thread类
创建子类,继承自Thread,并重写run方法
并不是创建好子类之后就会创建线程的,而是实例化之后调用start()方法才创建了线程
classMyThreadextendsThread{@Overridepublicvoidrun(){for(int i=0;i<10;i++){System.out.println("hello thread");try{Thread.sleep(1000);}catch(InterruptedException e){//线程被异常中断
e.printStackTrace();}}}}publicclassTest1{publicstaticvoidmain(String[] args){//MyThread t = new MyThread();//可以用Thread类接受实例,也可以使用MyThread接收实例Thread t =newMyThread();
t.start();for(int i=0;i<20;i++){System.out.println("main");try{Thread.sleep(500);}catch(InterruptedException e){
e.printStackTrace();}}}}
2.实现 Runnable 接口
创建一个类实现Runnable接口,实现run方法
运行方式:
- new一个实现了Runnable接口的类实例
- new 一个Thread(new Runnable);将 Runnable 对象作为 target 参数传入
- 调用类的.start()方法
- 此类方式比较推荐:Runnable单纯的只是描述了一个任务,至于任务是用什么方式来执行,Runnable并不关心(高内聚低耦合)
classMyRunnableimplementsRunnable{@Overridepublicvoidrun(){System.out.println("MyRunnable");}}publicclassTest2{publicstaticvoidmain(String[] args){Thread t =newThread(newMyRunnable());
t.start();}}
对比上面两种方法:
- 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
- 实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread()来引用线程实例
3.使用匿名内部类
此种方式输入方法1的变形
publicclassTest3{publicstaticvoidmain(String[] args){//匿名内部类创建 Thread 子类对象Thread thread =newThread(){@Overridepublicvoidrun(){System.out.println("Test3");}};
thread.start();}}
4.使用匿名内部类
此种方式输入方法2的变形
publicclassTest4{publicstaticvoidmain(String[] args){//匿名内部类创建 Runnable 子类对象Thread t1 =newThread(newRunnable(){@Overridepublicvoidrun(){System.out.println("t1");}});
t1.start();}}
3和4看似相同,实则完全是两种方式
3.匿名的是继承Thread的实现类类
4.匿名的则是实现Runnable的接口类
5.lambda表达式.
属于4.方式的lambda表达式方式.
此种方式虽然更简洁,但是对于初学者相对不友好
publicclassTest5{publicstaticvoidmain(String[] args){Thread t1 =newThread(()->System.out.println("lambda"));
t1.start();}}
Thread介绍
了解完线程的创建,我们再来看看Java的Thread类.
1.Thread常见的构造方法
方法说明Thread()创建线程对象Thread(Runnable target)使用 Runnable 对象创建线程对象Thread(String name)创建线程对象,并给线程对象命名Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
Thread t1 =newThread();Thread t2 =newThread(newMyRunnable());Thread t3 =newThread("这是我的名字");Thread t4 =newThread(newMyRunnable(),"这是我的名字");
2.Thread 的几个常见属性
属性获取方法IDgetId()名称getName()状态getState()优先级getPriority()是否后台线程isDaemon()是否存活isAlive()是否被中断isInterrupted()
- ID 是线程的唯一标识,不同线程不会重复.
- 名称是使用各种调试工具时,可以使用名称来区别线程.
- 状态表示线程当前所处的一个情况,指官方文档中的线程状态.
- 优先级高的线程理论上来说更容易被调度到,但并不是一定调用优先极高的线程.
- 后台线程,JVM就属于后台线程,会在一个进程的所有非后台线程结后,才会结束运行.
- 是否存活,可以简单理解为 run 方法是否运行结束.
- 可以理解为有一个标志位,用于控制线程是否被中断,而此方法则是根据标志位来判断线程是否被中断.
Thread方法介绍
1.启动线程
start()方法
Thread t1 =newThread(()->System.out.println("lambda"));
t1.start();
start()方法和run()方法的区别.
- 调用start()方法是区别于当前线程另外开启一个新的线程去运行对象中的run方法.当前线程的后续代码是和新开的线程"并发"运行的.
- 调用run()方法则是在当前线程中调用run()方法运行.并没有开启一个新的线程.当前线程中的后续代码则需要等待run()方法运行完才可以执行.
2.中断线程
- 通过共享的标记来进行沟通,设置标志位,通过在其他线程控制标志位,来终止线程
publicclassTest9{privatestaticvolatileboolean isQuit =false;publicstaticvoidmain(String[] args){Thread t1 =newThread(()->{int i=0;while(!isQuit){System.out.println("t1: "+(i++));try{Thread.sleep(700);}catch(InterruptedException e){
e.printStackTrace();}}});
t1.start();try{Thread.sleep(5000);}catch(InterruptedException e){
e.printStackTrace();}
isQuit =true;System.out.println("终止t1线程");}}
- 调用 interrupt() 方法来通知线程中断.
先来看两个获取线程标志位的方法
1.Thread.interrupted();
静态方法(一个程序只有一个标志位)
2.Thread.currentThread().isInterrupted();
实例方法,获取当前线程实例的标志位(每个线程有自己的标志位,推荐使用)
publicclassTest10{publicstaticvoidmain(String[] args){Thread t1 =newThread(()->{int i=0;while(!Thread.currentThread().isInterrupted()){System.out.println("t1: "+(i++));try{Thread.sleep(700);}catch(InterruptedException e){//e.printStackTrace();break;}}});
t1.start();try{Thread.sleep(5000);}catch(InterruptedException e){
e.printStackTrace();}//在主线程中调用 interrupt方法
t1.interrupt();//中断t1System.out.println("终止t1线程");}}
调用线程的t1.interrupt()方法,来改变标志位,该方法有两种情况,
- 线程就绪状态,就会设置标志位为true.
- 线程阻塞状态(sleep),就会触发一个异常(InterruptException). (解决方式)可以在catch块中使用break;中断线程
3.等待一个线程
join()方法
在a线程中调用b.join(),就是a线程阻塞等待b线程
join()方法在默认情况下是死等,可以给方法设置参数.单位为毫秒(超时时间)
publicclassTest11{publicstaticvoidmain(String[] args){Thread t1 =newThread(()->{for(int i=0;i<10;i++){System.out.println("t1: "+(i++));try{Thread.sleep(1000);}catch(InterruptedException e){
e.printStackTrace();}}});
t1.start();try{//main阻塞等待t1
t1.join();}catch(InterruptedException e){
e.printStackTrace();}System.out.println("main线程阻塞等待t1线程");}}
4.获取当前线程引用
Thread.currentThread();
静态方法:返回当前线程对象的引用
publicclassTest12{publicstaticvoidmain(String[] args){Thread thread =Thread.currentThread();System.out.println(thread.getName());}}
5.休眠当前线程
Thread.sleep(3 * 1000);
静态方法:参数为休眠时间,单位为毫秒
作用:休眠调用该方法的线程.
需要注意的是这里设置的时间是在这个时间内不可以唤醒该线程,而不是时间结束就唤醒该线程.休眠时间结束后会进入就绪队列等待调用.
版权归原作者 魚小飛 所有, 如有侵权,请联系我们删除。