http://t.csdn.cn/ybiwThttp://t.csdn.cn/ybiwT
那么本篇文章我们将了解一下什么是线程?线程与进程有哪些区别?线程应该怎么去编程?
目录
http://t.csdn.cn/ybiwThttp://t.csdn.cn/ybiwT
一、什么是线程+线程的概念
三、进程和线程的区别
3.1 java线程和操作系统线程的关系
四、线程的创建
五、需要主要的几个地方
六、Thread类里面的一些其他方法
6.1Thread几个常见属性
一、什么是线程+线程的概念
进程,是比较“重量的”速度慢/消耗资源多的,那么创建一个进程,成本比较高;销毁一个进程,成本也比较高;调度一个进程,成本也挺高的。进程包含线程,一个进程里可以有多个线程,多个线程之间也是并发执行的。
那么线程,则是更轻量的进程(轻量级进程),一个进程中可以包含多个线程,此时这多个线程每个线程都是一个独立可以调度执行的“执行流”,同时这些进程共用一份进程的系统资源,(意味着,对于线程而言,系统资源是已经分配好了的,创建线程就省下了分配资源开销)。一个线程就是一个 "执行流". 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 "同时" 执行着多份代码。
一个进程中的多个线程之间,共用同一份系统资源:
1.(内存空间)
2.(文件描述符表)
操作系统,真正调度的是在调度线程,而不是进程。
线程是操作系统调度运行的基本单位
进程是操作系统资源分配的基本单位
举例:滑稽老铁吃鸡
多进程吃鸡:
多线程吃鸡:
并不是线程越多越好,如果线程过多会影响系统的效率,如果一个线程出现了异常,可能会导致此进程中的所有线程全部崩溃!!!
二、为什么要有线程?
- 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU资源.
- 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程.
其次, 虽然多进程也能实现 并发编程, 但是线程比进程更轻量.
- 创建线程比创建进程更快.
- 销毁线程比销毁进程更快.
- 调度线程比调度进程更快.
最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 "线程池"(ThreadPool) 和 "协程"
(Coroutine)
三、进程和线程的区别
1.进程包含线程
2.进程有自己独立的内存空间和文件描述符表,同一个进程中的多个线程之间,共用同一份地址空间和文件描述符表
3.进程是操作系统资源分配的基本单位,线程是操作系统调度执行的基本单位
4.进程之间具有独立性,一个进程挂了,不会影响其他的进程;同一个进程里面的线程挂了,可能会有全部的线程都挂掉
3.1 java线程和操作系统线程的关系
线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库).
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装.
四、线程的创建
方法1:继承Thread类
1) 继承 Thread 来创建一个线程类.
class MyThread extends Thread { @Override public void run() { System.out.println("hello world"); } }
2) 创建 MyThread 类的实例
MyThread t = new MyThread();
3) 调用 start 方法启动线程
t.start(); // 线程开始运行
上述代码涉及俩个线程:
1.main方法所对应的线程(一个进程里面至少得有一个线程)也可以成为主线程
2.通过t.start()创建的新的线程
方法二:实现Runnable接口
1) 实现 Runnable 接口
class MyRunnable implements Runnable { @Override public void run() { System.out.println("这里是线程运行的代码"); } }
2) 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.
Thread t = new Thread(new MyRunnable());
3) 调用 start 方法
t.start(); // 线程开始运行
第一种写法是使用Thread的run描述线程入口
第二章写法是使用Runnable interface 来描述线程入口
没有本质区别
方法三:继承Thread,使用匿名内部类
public class ThreadDemo1 { public static void main(String[] args) { Thread t = new Thread(){ @Override public void run(){ while (true){ System.out.println("hello t"+ Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; t.start(); } }
第四种:实现Runnable,使用匿名内部类,内部类(定义在类里面的类)
public class MyRunnable2 { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("hello t"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); } }
{ 放到哪里就是针对哪个类创建的匿名内部类
第五种:使用Lambda表达式,最简单直观的方法
lambda表达式的基本写法:()->{ }
public class MyThreadDemo3 { public static void main(String[] args) { Thread t = new Thread(() -> { while (true){ System.out.println("hello t"+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); } }
五、需要主要的几个地方
1.sleep是Thread的静态方法,参数单位是ms,1s = 1000ms。
2.t.strat()是调用操作系统的api,创建新线程,新的线程里调用t.run,另外启动了一个执行流,新的线程来执行另一个while循环。
3.如果调用的是t.run(),而不是t.start(),那么只会有一个线程来执行,也就是只有一个循环,进入第一个死循环就出不去了,如下图:
4.run叫入口方法,是一个特殊的方法,只是重写了父类的方法,然后这个方法就被Java自动执行到了。
当我们的代码在运行时,可以使用JDK自身带的软件去查看线程
描述线程中的代码执行到哪里了
六、Thread类里面的一些其他方法
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
用我们上面的例子来看,每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。
方法 | 说明 |
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target, String name) | 使用 Runnable 对象创建线程对象,并命名 |
【了解】Thread(ThreadGroup group, Runnable target) | 线程可以被用来分组管理,分好的组即为线程组,这 个目前我们了解即可 |
6.1Thread几个常见属性
属性 | 获取方法 |
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
isDaemon()设置为true表示是后台线程,false是前台线程,前台线程会阻止java进程结束,必须得java进程中所有得前台线程都执行完,java进程才能结束,创建的线程默认是前台的。