一 线程入门
1.1 什么是进程 什么是线程
进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。
单位:1厘米 2天 1个进程 2个线程 3个事务
![ab6560a8385fef3bc7e765c61418c3e5.png](https://i-blog.csdnimg.cn/blog_migrate/2396f548eb0df40a55ff609628322663.jpeg)
1.2 什么是多线程
多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。
![efa9033e4353d0d1c349c702ccf29ec1.png](https://i-blog.csdnimg.cn/blog_migrate/c5dc1cdbc26da4ac42e997538cce0ef1.jpeg)
1.3 为什么要使用多线程
三岁小孩的脑筋急转弯: 煮一个鸡蛋 三分钟,请问 煮三个鸡蛋几分钟?
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。
![d2d4fda90a79b5a494867abf0f67598d.png](https://i-blog.csdnimg.cn/blog_migrate/4b36c30cd5edf02d4fa23229e7749752.jpeg)
1.4 开启线程的几种方式
A 没有线程的时候 代码执行
public static void main(String[] args) { for(int i=0;i<100;i++){ System.out.println("AAAA"+i); } for(int i=0;i<100;i++){ System.out.println("BBBB"+i); }}
没有多线程的时候 是先执行第一个for循环,执行完成之后才能执行第二个for循环
B 第一种方式的多线程
创建线程
第一步:创建一个类 继承Thread
第二步:重写父类的run函数,在run里面写的代码就会在分线程中执行
public class MyThread extends Thread { @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("BBBB"+i); } }}
调用分线程代码
第一步:创建线程类对象
第二步:调用 start()函数
public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); for(int i=0;i<1000;i++){ System.out.println("AAAA"+i); }}
(注意面试题:调用start和run有什么区别?)
![1d23fb678fc6e077f03de8afd5060ca1.png](https://i-blog.csdnimg.cn/blog_migrate/c2de0ceeaef2186a66fbd4a2015fc0c2.jpeg)
C 第二种方式创建分线程
线程创建
第一步:创建一个类 实现 Runnable接口
第二步:重写run函数。这就是线程函数
public class MyThread implements Runnable { @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("BBBB"+i); } }}
线程调用
第一步:创建线程类的对象
第二步: 创建Thread类对象 并且将线程类对象传递到Thread类中
第三步:调用thread对象的start方法
public static void main(String[] args) { MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start(); for(int i=0;i<1000;i++){ System.out.println("AAAA"+i); }}
D 第二种方式的变种写法
首先我们需要了解内部类和外部类
public class People { int age; /** 成员内部类 */ class Boy{ private int id; } /*静态内部类*/ static class Girl{} public void eat(){ /*局部内部类*/ class Dog{} /*匿名内部类 * 创建一个匿名类对象 这个匿名类实现了 Runnable接口 * */ Runnable a = new Runnable() { @Override public void run() { } }; }}/** * 外部类 */class Man {}
此时我们可以创建一个匿名内部类实现Runnable接口,交给系统的Thread类进行分线程处理.
public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { for(int i=0;i<1000;i++){ System.out.println("BBBB"+i); } } }).start(); for(int i=0;i<1000;i++){ System.out.println("AAAA"+i); }}
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
E 使用Callable形式
public static void main(String[] args) { FutureTask task = new FutureTask(new Callable() { @Override public Object call() throws Exception { for (int i = 0; i < 1000; i++) { System.out.println("第1个for循环"+i); } return null; } }); new Thread(task).start(); for (int i = 0; i < 1000; i++) { System.out.println("第2个for循环"+i); }}
(1)Callable规定的方法是call(),而Runnable规定的方法是run(). (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。 (3)call()方法可抛出异常,而run()方法是不能抛出异常的。 (4)运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。 它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。 Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
1.5 线程函数
public static void main(String[] args) { System.out.println("你好 世界"); new Thread(new Runnable() { @Override public void run() { long id = Thread.currentThread().getId(); //当前线程的id String name = Thread.currentThread().getName();//当前线程的名字 System.out.println(id+"-----"+name); } } , "hahahaha" ).start(); try { Thread.sleep(1000); // 线程休眠 } catch (InterruptedException e) { e.printStackTrace(); }}
Thread.sleep(1000); // 线程休眠
long id = Thread.currentThread().getId(); //当前线程的id String name = Thread.currentThread().getName();//当前线程的名字
1.6 守护线程
什么是守护线程?
所谓守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
守护线程和用户线程的没啥本质的区别:唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
在线程中分成两种情况:用户线程和守护线程。 我们可以理解成 用户线程是来执行业务操作的,守护线程是为用户线程提供服务的。
例如 我们程序在执行的时候 肯定有一个 main线程,他是一个主线程也是一个用户线程,还有一个线程叫做GC线程(垃圾回收线程)。
此时GC线程就是为主线程服务的----看主线程中执行操作的时候 有哪些垃圾产生,他要负责打扫垃圾。
在我们的程序中 代码执行会产生垃圾:内存中不再被使用的内存。
垃圾回收: 将内存中不使用的内存进行释放
垃圾回收的形式: 手动 (程序员自己通过代码进行释放内存 c) 和 自动(后台线程会定期清理 java)
GC线程:就是java后台运行的一种守护线程,定期会对被守护的线程进行垃圾回收操作。