目录

一、认识线程

1、概念

2、核心特点

3、进程和线程的区别

二、创建线程

1、继承Thread类

2、实现Runnable接口

3、匿名内部类创建Thread子类对象

4、匿名内部类创建Runnable子类对象

5、lambda表达式创建Runnable子类对象

三、Thread类及常见方法

1、Thread的常见构造方法

2、Thread的常见属性

ID是线程的唯一标识,不同线程会重复

3、启动、中断、等待一个线程

4、获取当前线程引用

5、休眠当前线程

四、线程的状态

1、线程的所有状态

2、线程状态和状态转移


一、认识线程

1、概念

       多线程是指在一个进程内,同时执行多个独立的代码片段(即线程),以提高程序的执行效率和资源利用率。

       简单来说,它就像一个工厂(进程),里面有多个工人(线程),他们共享工厂的设备(内存等资源),同时做不同的工作,从而更快地完成整体任务。


2、核心特点

资源共享:同一进程下的所有线程共享该进程的内存空间和资源,无需额外的通信机制

轻量级:线程的创建、切换和销毁的开销远小于进程,能更高效地利用CPU

并发执行:在单核CPU上,线程通过快速切换实现 " 并发 " ; 在多核CPU上,可实现真正的                      并行

主要应用场景:

  1. 提升响应速度:例如在下载文件(后台线程)的同时,允许用户继续操作界面        (UI线程)
  2. 提高CPU利用率:当一个线程因等待 I / O (如读取文件、网络请求)而阻塞时,其它线程可以继续使用CPU
  3. 处理批量任务:例如,同时处理多个网络请求、批量处理数据等


3、进程和线程的区别

  • 进程包含线程,每个进程至少有一个线程存在,即主线程
  • 每个进程都有自己独立的资源,进程和进程之间不能共享;同一个进程的线程之间,共享相同的资源
  • 进程之间不会相互影响,一个进程挂了,其它进程不会有事;同一个进程线程挂了,可能把其他线程一起带走
  • 进程之间通常(不绝对)不会有 “ 资源冲突 ” 情况;同一个进程的线程之间,特别容易出现资源访问冲突
  • 进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位


二、创建线程

1、继承Thread类

       继承Thread来创建一个线程类

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("hello MyThread");
    }
}
public class Demo1 {
    public static void main(String[] args) {
        MyThread t=new MyThread();
        t.start();//线程开始运行
    }
}


2、实现Runnable接口

       创建Thread类实例,调用Thread的构造方法时将Runnable对象作为target参数

class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("hello Runnable");
    }
}
public class Demo2 {
    public static void main(String[] args) {
        MyRunnable myRunnable=new MyRunnable();
        Thread t=new Thread(myRunnable);
        t.start();
    }
}


3、匿名内部类创建Thread子类对象

public class Demo3 {
    public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run() {
                System.out.println("使用匿名类创建 Thread 子类对象");
            }
        };
        t.start();
    }
}


4、匿名内部类创建Runnable子类对象

public class Demo4 {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("使用匿名类创建 Runnable 子类对象");
            }
        });
        t.start();
    }
}


5、lambda表达式创建Runnable子类对象

public class Demo5 {
    public static void main(String[] args) {
        Thread t=new Thread(() ->{
            System.out.println("hello lambda");
        });
        t.start();
    }
}


三、Thread类及常见方法

1、Thread的常见构造方法

方法 说明
Thread() 创建线程对象
Thread(Runnable target) 使用 Runnable 对象创建线程对象
Thread (String name) 创建线程对象并命名
Thread(Runnable target,String name) 使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group,Runnable target) 线程可以被用来分组管理,方便对一组线程进行统一的操作
public class Demo6 {
    public static void main(String[] args) {
        Thread t1=new Thread();

        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                System.out.println("MyRunnable");
            }
        };
        Thread t2=new Thread(runnable);

        Thread t3=new Thread("我的名字");

        Thread t4=new Thread(runnable,"我的名字");

    }
}


2、Thread的常见属性

属性 获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()
  • ID是线程的唯一标识,不同线程会重复

  • 名称是各种调试工具用到
  • 状态表示线程当前所处的一个情况
  • 优先级高的线程理论上来说更容易调度到
  • 关于后台线程:JVM会在一个进程的所有非后台线程结束后才运行结束
  • 是否存活,简单来说为 run 方法是否运行结束了
  • 线程中断问题,下面进一步说明
public class Demo7 {
    public static void main(String[] args) {
        Thread t=new Thread(()->{
            for (int i = 0; i < 10 ; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName()+"即将死亡");
        });

        System.out.println(Thread.currentThread().getName()+" ID: "+t.getId());
        System.out.println(Thread.currentThread().getName()+" 名称: "+t.getName());
        System.out.println(Thread.currentThread().getName()+" 状态: "+t.getState());
        System.out.println(Thread.currentThread().getName()+" 优先级: "+t.getPriority());
        System.out.println(Thread.currentThread().getName()+" 是否后台线程: "+t.isDaemon());
        System.out.println(Thread.currentThread().getName()+" 是否存活: "+t.isAlive());
        System.out.println(Thread.currentThread().getName()+" 是否被中断: "+t.isInterrupted());
        t.start();
    }
}


3、启动、中断、等待一个线程

      启动一个线程 - start()

      启动线程的核心是调用线程对象的 start ( ) 方法 (而非直接调用 run( ) 方法),该方法会通知操作系统为线程分配资源并调度执行,最终自动触发 run( ) 方法中的任务逻辑

public class Demo8 {
    public static void main(String[] args) {
        Thread t=new Thread(() ->{
            System.out.println("start线程启动");
        });
        t.start();
        System.out.println("main线程");
    }
}

        中断一个线程

有两种常见方式:

  1. 通过共享的标记来进行沟通
  2. 调用 interrupt( ) 方法来通知

通过标记:

public class Demo9 {
    public static boolean flag=false;
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(() ->{
           while(!flag){
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               System.out.println("未中断");
           }
            System.out.println(Thread.currentThread().getName()+"已中断");
        });

        t.start();
        Thread.sleep(10000);
        flag=true;
    }
}

通过已有的方法:

Thread内部包含了一个boolean类型的变量作为线程是否被中断的标记

方法 说明

public void

interrupt()

中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位

public static boolean

interrupted()

判断当前线程的中断标志位是否设置,调用后清除标志位

public boolean

isInterrupted()

判断对象关联的线程标志位是否设置,调用后不清除标志位
public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(() ->{
            Thread cur=Thread.currentThread();
            while (!cur.isInterrupted()){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //这里如果不break,那么sleep被唤醒后会重置标志位,会重新进入循环
                    break;
                }
                System.out.println("未中断");
            }
            System.out.println("被中断");
        });
        t.start();
        Thread.sleep(1500);
        t.interrupt();
    }
}

        等待一个线程 - join( )

方法 说明
public void join() 等待线程结束
public void join(long millis) 等待线程结束,最多等millis毫秒
public void join(long millis,int nanos) 同理但可以更高精度

当前线程(调用 join( ) 的线程)等待目标线程执行完毕,再执行自身后续的代码

public class Demo11 {
    public static int count=0;
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(() ->{
            for (int i = 0; i <=100 ; i++) {
                count+=i;
            }
        });
        t.start();
        //这里的join方法是为了让main线程等待Thread0线程执行完毕后再执行main线程的逻辑
        //如果没有join这个方法后续的count打印就是0
        t.join();
        System.out.println(count);
    }
}
public class Demo12 {
    public static int sum=0;
    public static void main(String[] args) throws InterruptedException {
        Thread mainThread=Thread.currentThread();
        Thread t=new Thread(() ->{
            try {
                //t线程也可以等待main线程
                mainThread.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(sum);
        });
        t.start();
        Thread.sleep(1000);
        for (int i = 0; i <=100 ; i++) {
            sum+=i;
        }
    }
}


4、获取当前线程引用

方法 说明
public static Thread currentThread() 返回当前线程对象的引用
public class Demo13 {
    public static void main(String[] args) {
        Thread t=Thread.currentThread();
        System.out.println(t.getName());
    }
}


5、休眠当前线程

方法 说明
public static void sleep (long millis) throws InterruptedException

休眠当前线程millis 毫秒

public static void sleep (long millis,int nanos) throws InterruptedException 可以更高精度的休眠

public class Demo14 {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3000);
        System.out.println(System.currentTimeMillis());
    }
}


四、线程的状态

1、线程的所有状态

  • NEW(新建状态):创建了 Thread 对象,未调用 start
  • TERMINATED(终止状态):操作系统内部线程已销毁,但 Thread 对象还在,线程入口方法执行完毕
  • RUNNABLE(就绪状态):正在工作中和即将开始工作
  • WAITING(阻塞状态 - > 等待):死等进入阻塞
  • TIMED_WAITING(超时等待):带有超时时间的阻塞等待
  • BLOCKED(阻塞):锁竞争 / 锁冲突


2、线程状态和状态转移

NEW、RUNNABLE、TERMINATED状态的转换:

public class Demo15 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(() ->{
            System.out.println("hello t");
            while (true){

            }
        });
        System.out.println(t.getState());//NEW
        t.start();
        Thread.sleep(1);
        System.out.println(t.getState());//RUNNABLE
        //如果没有while循环那么就是TERMINATED
        //System.out.println(t.getState());//TERMINATED
    }
}

WAITING、TIMED_WAITING:

public class Demo16 {
    public static void main(String[] args) throws InterruptedException {
        Thread mainThread=Thread.currentThread();
        Thread t=new Thread(() ->{
           while (true){
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               //System.out.println(mainThread.getState());//WAITING
               System.out.println(mainThread.getState());//TIMED_WAITING
           }
        });
        t.start();
        //t.join()
        t.join(10000);
    }
}

关于BLOCKED我们后续再根据锁来了解


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐