Java多线程(一)
一. 什么是线程
- 线程(Thread)是操作系统进行任务调度和执行的基本单位,它隶属于进程,是进程内部可独立运行的“子任务单元”。
- 线程也叫轻量级进程。(相较于进程,创建线程不需要申请更多的资源)
- 进程包含了线程。
- 一个线程就是一个“执行流”,多个线程之间可以同时执行“多份“代码。
二. 线程与进程的区别
- 进程是包含线程的,一个进程至少包含一个线程(称为主线程).
- 进程与进程之间不共享资源(内存空间),而在同一个进程中的线程之间共享资源(内存空间).
- 进程是操作系统分配资源的基本条件.
- 线程是操作系统调度执行的基本条件.
- 进程与进程之间不会相互影响,而同一个进程中的线程可能会相互影响(多线程编程中,多个线程可能会操作同一数据,这样会产生冲突,导致程序出现bug----线程安全问题)
注意:
- 增加线程的数量确实会提高代码的效率,但是当线程的数量提升超过一个度,效率不一定会提升,反而可能会降低(CPU的核心数是有限的,调度开销也会影响执行效率)
- Java生态中不鼓励使用多线程编程,非常鼓励多线程
三. 多线程
- 线程是操作系统中的概念,对于多线程的相关操作,操作系统提供了对应的API(类/方法)
- 每个线程都是一个独立的"执行流"
- 多个线程是"并发"执行的
3.1 创建线程
3.1.1 方法1:继承Thread类
- 继承Thread类
- 创建MyThread类的实例
- 重写父类(Thread类)的 run()方法
- 调用start方法启用线程
public class MyThread extends Thread{@Overridepublic void run() {while (true){System.out.println("MyThread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class Dom {public static void main(String[] args) {MyThread r=new MyThread();//Thread r=new MyThread();r.start();while (true){System.out.println("hi");}}
}
运行代码如下:
注意:
- 我们要执行 r 中的 run 方法时,不是直接调用的 r 的 run()方法,而是调用的 r.start() (调用Thread类的start()方法,该方法执行时会调用操作系统API在系统内部建立一个线程,当线程建立好后,就会自动执行上面重写的run方法)
- main方法自身也对应一个线程(操作系统自动创建),也称为主线程(注意:多线程编译中不存在父/子线程的说法)
- 为防止运行循环时过快,CPU达到极限,我们可以写一个Thread.sleep()方法(Thread类的一个静态方法),让线程陷入阻塞状态,降低CPU的消耗
- 多线程的调度是由操作系统负责的,操作系统对线程的调度可以认为时“随机”的
问:r.run( ) 与 r.start( ) 的区别:
使用 r.run( ) 时就不存在多线程,即一个执行流,就会导致先执行r.run( ),再执行后面的代码—单线程
3.1.2 方法2:实现Runnable接口
- 实现Runnable接口
- 重写run方法
- 创建MyRunnable实例对象
- 创建Thread类实例对象,调用Thread类的构造方法,将MyRunnable实例对象作为参数
public class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("MyRunnable");}
}public class text {public static void main(String[] args) {MyRunnable r=new MyRunnable();Thread t=new Thread(r);t.start();System.out.println("hi");}
}
注意:
- Runnable接口本身不能直接创建线程,需要借助Thread类来实现
- Runnable自身一般不会单独执行,要搭配“载体”
- 将MyRunnable实例对象作为参数后,调用start方法时实际是执行的MyRunnable中重写的run方法
3.1.3 方法3:匿名内部类创建Thread子类对象
- 创建Thread类的子类(没有名字-匿名)
- 重写 run()方法
- 创建Thread的子类的实例,并且使用t引用指向
public class text {public static void main(String[] args) {Thread r=new Thread(){@Overridepublic void run() {System.out.println("创建新线程");}};r.start();}
}
记住:调用start()才是真正的创建线程
3.1.4 方法4:匿名内部类创建Runnable子类对象
Thread t=new Thread(new Runnable() {@Overridepublic void run() {System.out.println("创建新线程");}});t.start();}
3.1.5 方法5:lambda创建Runnable子类对象【推荐】
Thread m=new Thread(()->{System.out.println("创建新线程");});m.start();
- () -> {... } 是 Lambda 表达式, () 表示 run 方法没有参数, {... } 内是 run 方法的具体实现逻辑,这里通过循环打印信息模拟线程任务。 - 创建 Thread 对象时,将 Lambda 表达式传入, Thread 会在启动后执行该 Lambda 表达式所代表的 run 方法逻辑。 - 调用 thread.start() 启动线程,线程进入就绪状态,等待 CPU 调度执行,同时主线程继续执行自己的逻辑,实现多线程并发。