0


Java多线程(1)

线程是什么

Java中一个线程就是一个 “执行流”. 每个线程有自己的任务. 多个线程之间 “同时” 各自完成着自己的任务.

线程和进程的区别

  1. 进程包含线程,一个进程里可以有一个线程,也可以有多个线程.
  2. 进程和线程都是为了处理并发编程这样的场景.但是进程有问题,频繁的穿件和释放的时候效率低,相比之下,线程更轻量,创建和释放效率更高.(轻量的原因是减少了申请释放资源的过程)
  3. 操作系统创建进程,要给进程分配资源,进程是操作系统分配资源的进本单位.而操作系统创建线程,是要在CPU上调度执行,线程是操作系统调度执行的基本单位.
  4. 进程具有独立性.每个进程有各自的虚拟地址空间,一个进程挂了,不会影响到其他进程.而同一个进程中的多个线程则是共用的一个内存空间,一个线程挂了,就有可能影响到其他线程,甚至导致整个进程奔溃.

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方法
运行方式:

  1. new一个实现了Runnable接口的类实例
  2. new 一个Thread(new Runnable);将 Runnable 对象作为 target 参数传入
  3. 调用类的.start()方法
  • 此类方式比较推荐:Runnable单纯的只是描述了一个任务,至于任务是用什么方式来执行,Runnable并不关心(高内聚低耦合)
classMyRunnableimplementsRunnable{@Overridepublicvoidrun(){System.out.println("MyRunnable");}}publicclassTest2{publicstaticvoidmain(String[] args){Thread t =newThread(newMyRunnable());
        t.start();}}

对比上面两种方法:

  1. 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
  2. 实现 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()方法,来改变标志位,该方法有两种情况,

  1. 线程就绪状态,就会设置标志位为true.
  2. 线程阻塞状态(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);
静态方法:参数为休眠时间,单位为毫秒
作用:休眠调用该方法的线程.
需要注意的是这里设置的时间是在这个时间内不可以唤醒该线程,而不是时间结束就唤醒该线程.休眠时间结束后会进入就绪队列等待调用.


本文转载自: https://blog.csdn.net/m0_58154870/article/details/127490287
版权归原作者 魚小飛 所有, 如有侵权,请联系我们删除。

“Java多线程(1)”的评论:

还没有评论