线程
文章目录
进程与线程:(线程存活在进程当中)
进程:程序的执行过程(电脑的任务管理器里面的就是进程)
特点:1、独立性:每个进程都有自己的空间,没有经过本进程的允许,一个进程不可以直接访问其他进程的空间
2、动态性:进程动态产生,动态消亡
3、并发性:任何进程都可以与其他进程一起并发执行(对于一个cpu来说是并发,对于多个cpu来说是并行
并发 | 同一时刻,多个指令在一个CPU上交替执行 |
---|---|
并行 | 同一时刻,多个指令在多个CPU上同时执行 |
**线程:**进程(应用程序)可以同时执行多个任务,每个任务就是线程
**多线程的意义:**多线程就是多个任务,一个线程在一个时刻只能运行在一个处理器核心上,电脑性能再好,进程为单线程也只是在一个核心上运行,发挥不了性能得到优势,而多线程可以发挥电脑性能的优势
1、提高执行效率
2、同时处理多个任务
JAVA开启线程的方式
-
继承Thread类
public class ThreadDemo1 { /* 开启线程第一种方式: 继承Thread类 1. 编写一个类继承Thread 2. 重写run方法 3. 将线程任务代码写在run方法中 4. 创建线程对象 5. 调用start方法开启线程 细节: 调用start方法开启线程, 会自动的调用run方法执行. 注意: 只有调用了start方法, 才是开启了新的线程 */ public static void main(String[] args) { // 4. 创建线程对象 MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); // 5. 调用start方法开启线程 mt1.start();//mt1.run()和mt1.start()输出结果相同但是mt1.run()没有开启线程 mt2.start(); } } // 1. 编写一个类继承Thread class MyThread extends Thread { // 2. 重写run方法 @Override public void run() { // 3. 将线程任务代码写在run方法中 for (int i = 1; i <= 200; i++) { System.out.println("线程任务执行了" + i); } } } ————————————————————————————————————————————————————————————————————————————————————————————————————————— public class ThreadDemo2 { /* Java程序默认是多线程的, 程序启动后默认会存在两条线程 1. 主线程 2. 垃圾回收线程 */ public static void main(String[] args) { for (int i = 1; i <= 2000; i++) { System.out.println("main线程执行了"); } MyThread mt = new MyThread(); mt.start(); //因为默认线程里面有一个垃圾回收线程,所以只省下一个线程来执行程序,又因为程序自上而下运行,所以当自己创建的线程在主方法下面时,执行for循环时,并没有创建自己写的线程,所以输出结果为2000次循环,再是自己的mt线程,不会出现交替执行的情况,如果自己创建的线程在for循环上面,则会出现交替执行的情况 } } class Demo { @Override protected void finalize() throws Throwable { System.out.println("垃圾被清理了"); } }
-
实现Runnable接口(扩展性更强):适用于线程任务无返回值
因为java中的继承只支持单继承,所以当类已经继承了一个父类,则无法再继承Thread类,所以只能通过接口实现多线程
public class ThreadDemo3 { /* 开启线程的第二种方式: 实现Runnable接口 1. 编写一个类实现Runnable接口 2. 重写run方法 3. 将线程任务代码写在run方法中 4. 创建线程任务资源 5. 创建线程对象, 将资源传入 6. 使用线程对象调用start方法, 开启线程 */ public static void main(String[] args) { // 4. 创建线程任务资源 MyRunnable mr = new MyRunnable(); // 5. 创建线程对象, 将资源传入 Thread t = new Thread(mr);//Thread类的构造方法中,可以接收一个Runnable类型,本质还是使用 Thread类创建线程 // 6. 使用线程对象调用start方法, 开启线程 t.start(); for (int i = 1; i <= 2000; i++) { System.out.println("main线程执行了"); } } } // 1. 编写一个类实现Runnable接口 class MyRunnable implements Runnable { // 2. 重写run方法 @Override public void run() { // 3. 将线程任务代码写在run方法中 for (int i = 1; i <= 200; i++) { System.out.println("线程任务执行了" + i); } } }
-
实现Callable接口(扩展性同上):适用于线程任务有返回值
继承Thread类和实现Runnable接口的方法都没有返回值,若线程执行完有个结果需要返回则使用Callable接口实现多线程
public class ThreadDemo4 { /* 开启线程的第三种方式: 实现Callable接口 1. 编写一个类实现Callable接口 2. 重写call方法 3. 将线程任务代码写在call方法中 4. 创建线程任务资源对象 5. 创建线程任务对象, 封装线程资源 6. 创建线程对象, 传入线程任务 7. 使用线程对象调用start开启线程 */ public static void main(String[] args) throws Exception { // 创建线程任务资源对象 MyCallable mc = new MyCallable(); // 创建线程任务对象, 封装线程资源(媒介) FutureTask<Integer> task1 = new FutureTask<>(mc);//FutureTask本质上继承了Runnable类,所以可以可以用Thread接收FutureTask对象; FutureTask<Integer> task2 = new FutureTask<>(mc);//在Callable接口中,和另外两种方法相比,每打开一个线程就要加一句FutureTask //why?每条线程应该都有结果返回,所以每个结果应该有对应的FutureTask来接收结果,才能用各自的对象去调用get方法,不然就相当于在一个FutureTask将其内的内容拿了多次(让一个人买了一瓶水,从他手里反反复复拿多次 // 创建线程对象, 传入线程任务 Thread t1 = new Thread(task1); Thread t2 = new Thread(task2); // 使用线程对象调用start开启线程 t1.start(); t2.start(); Integer result1 = task1.get();//返回的结果用线程任务对象接 Integer result2 = task2.get();//该类代码只能在开线程之后,因为他要等着接收结果,但是线程没开没有结果 System.out.println("task1获取到的结果为:" + result1); System.out.println("task2获取到的结果为:" + result2); } } // 1. 编写一个类实现Callable接口 class MyCallable implements Callable<Integer> {//返回值是什么类型,泛型就是什么类型 // 2. 重写call方法 @Override public Integer call() throws Exception { // 3. 将线程任务代码写在call方法中 int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; System.out.println("sum=" + sum); } return sum; } }