Java多线程基础知识
多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。
多线程的优点:
- 让多部分代码同时执行。
- 带来良好的用户体验。
- 充分利用CPU的资源。
- 简化编程模型。
多线程的缺点: 导致程序运行效率降低。
进程和线程的基本认识
进程:
- 进程是一个正在执行的程序,一个程序可以同时执行多个任务(线程)。
- 进程独占内存空间,同时保持各自的运行状态,相互之间不会干扰。
- 进程是并发执行程序过程资源分配和管理的基本单位(资源分配的最小单位)。
- 每个进程都有自己独立的地址空间,每启动一个进程,系统就会分配地址空间。
线程:
- 通常,每一个任务称作一个线程,线程有时候会被成为轻量级的进程。
- 它是程序执行的最小单位,一个进程可以拥有多个线程,多个线程之间共享进程的地址空间以及一些进程级别的其他资源,但是各个线程拥有自己的栈空间。
进程和线程的区别和联系?
- 1、进程是资源分配的最小单位,线程是程序执行(CPU调度)的最小单位。
- 2、进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
- 3、线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
- 4、但是多进程程序更健壮,多线程程序只要有一个线程死掉,那么对于其共享资源的其他线程也会产生影响,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
进程和线程的使用场景?
因为进程是资源分配的基本单位,线程是程序执行的最小单元。所有我们应该从这两点以及进程与线程之间的健壮性来考虑。
- 1、在程序中,如果需要频繁创建和销毁的使用线程。因为进程创建和销毁开销很大(需要不停的分配资源),但是线程频繁的调用只是改变CPU的执行,开销小。
- 2、如果需要程序更加的稳定安全时,可以选择进程。如果追求速度,就选择线程。
并发和并行的区别
- 并发指的是多个线程操作一个资源,不是同时执行,而是交替执行,单核CPU,只不过因为CPU的时间片很短,速度太快,看起来同时执行。
- 并行才是真正的同时执行,多核CPU,每一个线程都可以使用一个单独的CPU资源来运行。
相关概念:
- QPS:每秒能够响应的请求数。
- 平均响应时间:并发数 / 平均响应时间 = QPS。
- 并发用户数:系统可以承载的最大用户量。
- 吞吐量:单位时间内能够处理的请求数。
互联网系统架构中,如何提高系统的并发能力?
垂直扩展:
提升单机的处理能力。
- 1、增强单机的硬件性能:增加CPU的核数、内存升级、磁盘扩容。
- 2、提升系统的架构能力:使用Cache来提高效率。
水平扩展:
集群、分布式都是水平的扩展方案。
- 集群:多个人做同一事(例如一个餐厅中同时多顾几个厨师同时炒菜)。
- 分布式:一个复杂的事情,拆分成几个简单的步骤,分别找不同的人去完成(1.洗菜 2.切菜 3.炒菜)。
具体操作为:
- 1、站点层扩容:通过Nginx反向代理,实现高并发的系统,将服务部署在多个服务器上。
- 2、服务层扩容:通过RPC框架实现远程调用:Dubbo,Spring Clodud,将业务逻辑分拆成不同的RPC Client,每个Clident完成各自的不同的业务,如果并发量比较大,则可以新增加RPC Client。
- 3、数据层扩容:一台数据库拆分成多态,分库分表,主从复制,读写分离。
创建线程
我们以一边看电视、一边吃饭为例、看看多线程情况下如何编写程序?
继承Thread类
继承Thread类,重写run()方法(以下是当前线程的执行逻辑)。
class WatchTV extends Thread {
@Override
public void run() {
System.out.println("Watch TV");
}
}
class Eat extends Thread {
@Override
public void run() {
System.out.println("eating");
}
}
public class TestDemo {
public static void main(String[] args) {
Thread watchTV = new WatchTV();
Thread eat = new Eat();
watchTV.start(); //watchTV();
eat.start(); //eat();
}
}
实现Runnable接口
实现Runnable接口,重写run()方法(以下是当前线程的执行逻辑)。
class WatchTV implements Runnable {
@Override
public void run() {
System.out.println("Watch TV");
}
}
class Eat extends implements Runnable {
@Override
public void run() {
System.out.println("eating");
}
}
public class TestDemo {
public static void main(String[] args) {
Thread watchTV = new Thread(new WatchTV());
Thread eat = new Thread(new Eat());
watchTV.start(); //watchTV();
eat.start(); //eat();
}
}
匿名内部类
public class TestDemo {
public static void main(String[] args) {
new Thread("WatchTV") {
@Override
public void run() {
System.out.println("Watch TV");
}
}.start();
new Thread("Eat")