线程简介
程序:程序就像一堆写好的指令和数据放在一起,它是静止的,不会自己动起来。
进程(Process):进程是把程序真正运行起来的过程,它是动态的,系统会给它分配各种资源,比如内存等。
线程(Thread):一个进程里通常会有好几个线程,最少也得有一个,不然进程就没啥用了。线程是 CPU 安排干活和实际执行任务的小单位。
注意事项:很多时候说的多线程其实是模拟出来的,真正的多线程得有多个 CPU(就是多核,像服务器那种)。要是模拟的多线程,只有一个 CPU 的时候,同一瞬间 CPU 只能执行一个代码,不过因为它切换得特别快,就好像是同时在执行好多事一样,但其实是一种错觉。
- 线程就像独立的做事通道。
- 程序运行时,就算你不创建,后台也有主线程、gc 线程等好多线程。
- main () 是主线程,是程序开始运行的地方,负责执行整个程序。
- 一个进程里开了多个线程,啥时候运行由调度器安排,调度器和操作系统关系密切,顺序不能随便改。
- 多个线程操作同一份资源会抢资源,得用并发控制。
- 用线程会有额外花费,像 CPU 安排线程的时间和并发控制的花费。
- 每个线程在自己的工作内存里交流,内存管不好会让数据乱套。
线程实现(重点)
- 线程创建
继承Thread类
- 自定义线程类继承
Thread
类 - 重写
run()
方法,编写线程执行体 - 创建线程对象,调用
start()
方法启动线程
public class MyThread extends Thread {// 重写Thread类的run方法,run方法里是线程要执行的任务@Overridepublic void run() {// 这里写线程要执行的具体任务for (int i = 0; i < 20; i++) {System.out.println("自定义线程1正在运行:" + i);}}
}public class ThreadExample {public static void main(String[] args) {// 创建自定义线程的对象MyThread myThread = new MyThread();// 启动线程,注意不是调用run方法,而是调用start方法,调用start方法后会自动调用run方法myThread.start();}
}
首先,我们定义了一个 MyThread
类,它继承自 Thread
类。在 MyThread
类中,重写了 run
方法。run
方法是线程的执行体,当线程启动后,会自动调用这个方法执行具体的任务。
在 run
方法中,我们使用了一个 for
循环打印了一些信息,这就是这个线程要执行的具体任务。
然后,在 ThreadExample
类的 main
方法中,创建了 MyThread
类的对象 myThread
。
最后,调用 myThread.start()
方法来启动线程。这里要注意的是,不能直接调用 run
方法,因为直接调用 run
方法只是普通的方法调用,而不会开启新的线程,只有调用 start
方法,Java 虚拟机才会为这个线程分配资源并启动线程,进而调用 run
方法。
实现Runnable接口(推荐使用)
- 定义
MyRunnable
类实现Runnable
接口 - 实现
run()
方法,编写线程执行体 - 创建线程对象,调用
start()
方法启动线程
public class MyRunnable implements Runnable {// 实现 Runnable 接口的 run 方法,该方法包含线程要执行的任务@Overridepublic void run() {// 这里是线程要执行的具体任务for (int i = 0; i < 10; i++) {System.out.println("自定义线程正在运行:" + i);}}
}public class RunnableExample {public static void main(String[] args) {// 创建 MyRunnable 类的实例MyRunnable myRunnable = new MyRunnable();// 创建 Thread 对象,并将 MyRunnable 实例作为参数传递给它Thread thread = new Thread(myRunnable);// 启动线程thread.start();}
}
首先,我们创建了一个名为 MyRunnable
的类,它实现了 Runnable
接口。在 MyRunnable
类中,重写了 run
方法。run
方法是 Runnable
接口中唯一的抽象方法,当线程启动后,它将执行 run
方法中所包含的任务。
在 run
方法中,我们使用了一个 for
循环,从 0 到 9 打印信息,这就是该线程要执行的具体任务。
然后,在 RunnableExample
类的 main
方法中,创建了 MyRunnable
类的实例 myRunnable
。
接着,我们创建了一个 Thread
对象 thread
,并将 myRunnable
作为参数传递给它。这是因为 Thread
类的构造函数可以接受一个 Runnable
接口的实例,这样 thread
线程启动后,会执行 myRunnable
的 run
方法。
最后,调用 thread.start()
方法启动线程。
这种实现 Runnable
接口的方式是 Java 中创建线程的一种常用方式,与继承 Thread
类相比,实现 Runnable
接口的优势在于避免了 Java 单继承的限制,使代码更加灵活,因为一个类可以实现多个接口,但只能继承一个父类。
另外,使用 Runnable
接口可以方便地将任务和线程对象分离,在需要将任务共享给多个线程时,只需要创建多个 Thread
对象,并将同一个 Runnable
实例传递给它们,就可以让多个线程执行相同的任务。例如,如果我们想让多个线程执行相同的 run
方法,可以这样做:
public class RunnableExample {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread1 = new Thread(myRunnable);Thread thread2 = new Thread(myRunnable);thread1.start();thread2.start();}
}
上述代码创建了两个 Thread
对象 thread1
和 thread2
,它们都执行 myRunnable
的 run
方法,实现了多个线程执行相同任务的目的。
实现Callable接口(了解即可)
- 实现
Callable
接口,需要返回值类型 - 重写
call
方法,需要抛出异常 - 创建目标对象
- 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行:Future
result1 = ser.submit(t1); - 获取结果:boolean r1 = result.get()
- 关闭服务:ser.shutdownNow()
- Lamda 表达式
Lambda
表达式是 Java 8 引入的一个新特性,它可以让你用一种简洁的方式来表示一个匿名函数。就像是一种简洁的代码书写方式,让你能更方便地编写一些功能简单的函数,而不用像以前那样,为了一个小功能去创建一个完整的类或方法。
通常的格式是 (参数列表) -> {函数体}
。比如说,如果你想定义一个对两个整数求和的函数,传统的写法可能要写一个完整的方法,但是用 Lambda
表达式可以写成 (int a, int b) -> {return a + b;}
。
Lambda
表达式的优点:
- 简洁性:能大大减少代码量,让代码看起来更简洁,读起来也更清晰,尤其是对于那些功能比较简单的函数,以前可能需要写好几行,现在用 Lambda 就能在一行里搞定。
- 功能性:可以作为参数传递给其他方法,在使用 Java 的一些函数式接口(只有一个抽象方法的接口)时特别有用。比如 Java 中的
Runnable
接口,以前你可能需要创建一个类来实现它,现在可以直接使用 Lambda 表达式。
例如,使用 Lambda 表达式创建一个 Runnable
线程,传统写法是:
// 匿名内部类,没有类的名称,必须借助接口或者父类。原本Runnable接口使用MyRunnable类来实现。
Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println("线程正在运行");}
};
使用 Lambda 表达式后可以写成:
Runnable runnable = () -> {System.out.println("线程正在运行");
};
下面是
lambda
大致推导过程(供参考):
public class LambdaDemo {// 3.静态内部类:static修饰,类里面定义类static class Like2 implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda2");}}public static void main(String[] args) {ILike like = new Like();like.lambda();like = new Like2();like.lambda();// 4.局部内部类:方法里面定义类class Like3 implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda3");}}like = new Like3();like.lambda();// 5.匿名内部类:没有类的名称,必须借助接口或者父类like = new ILike() {@Overridepublic void lambda() {System.out.println("i like lambda4");}};like.lambda();// 6.用 lambda 简化:接口和方法都不要了,只留(参数列表) -> {函数体}like = () -> {System.out.println("i like lambda5");};like.lambda();}
}// 1.定义一个函数式接口:一种特殊接口,只包含一个抽象方法
interface ILike{void lambda();
}// 2.类实现接口
class Like implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda");}
}