这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
引言:本文将以线程的基础概念作为开篇,线程设计知识点很多,理论抓起,随后实践出真知。
实现多线程是采用一种并发执行机制。
并发执行机制原理:简单地说就是把一个处理器划分为若干个短的时间片,每个时间片依次轮流地执行处理各个应用程序,由于一个时间片很短,相对于一个应用程序来说,就好像是处理器在为自己单独服务一样,从而达到多个应用程序在同时进行的效果。
多线程就是把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序划分为若干个子任务,多个子任务并发执行,每一个任务就是一个线程。这就是多线程程序。
基础概念
什么是进程和线程
进程是程序运行资源分配的最小单位 进程是操作系统进行资源分配的最小单位,其中资源包括:CPU、内存空间、磁盘 IO 等,同一进程中的多条线程共享该进程中的全部系统资源,而进程和进程之间是相互独立的。进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。显然,程序是死的、静态的,进程是活的、动态的。进程可以分为系统进程和用户进程。凡是用于完成操作系统的各种功能的进程就是系统进程,它们就是处于运行状态下的操作系统本身,用户进程就是所有由你启动的进程。
线程是 CPU 调度的最小单位,必须依赖于进程而存在 线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的、能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
CPU核心数和线程数的关系
多核心:也指单芯片多处理器( Chip Multiprocessors,简称 CMP),CMP 是由美国斯坦福大学提出的,其思想是将大规模并行处理器中的 SMP(对称多处理器)集成到同一芯片内,各个处理器并行执行不同的进程。这种依靠多个 CPU 同时并行地运行程序是实现超高速计算的一个重要方向,称为并行处理。 简单地说,CPU的核心数是指物理上,也就是硬件上存在着几个核心。比如,双核就是包括2个相对独立的CPU核心单元组,四核就包含4个相对独立的CPU核心单元组,等等,依次类推。相关文章---> ubuntu下cpu数,核心数,线程数的关系
多线程: Simultaneous Multithreading.简称 SMT.让同一个处理器上的多个线程同步执行并共享处理器的执行资源。
核心数、线程数:目前主流 CPU 都是多核的。增加核心数目就是为了增加线程数,因为操作系统是通过线程来执行任务的,一般情况下它们是 1:1 对应关系,也就是说四核 CPU 一般拥有四个线程。
CPU时间篇轮转机制简述
我们平时在开发的时候,感觉并没有受 cpu 核心数的限制,想启动线程就启动线程,哪怕是在单核 CPU 上,为什么?这是因为操作系统提供了一种 CPU 时间片轮转机制。时间片轮转调度是一种最古老、最简单、最公平且使用最广的算法,又称 RR调度。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法。每个进程被分配一时间段,称作它的时间片,即该进程允许运行的时间。
并行和并发
并发::指应用能够交替执行不同的任务,比如单 CPU 核心下执行多线程并非是 同时执行多个任务,如果你开两个线程执行,就是在你几乎不可能察觉到的速度不 断去切换这两个任务,已达到"同时执行效果",其实并不是的,只是计算机的速度太 快,我们无法察觉到而已。\
并行:指应用能够同时执行不同的任务,例:吃饭的时候可以边吃饭边打电话, 这两件事情可以同时执行 区别:一个交替执行,一个同时执行。
高并发编程的注意事项
线程之间的安全性 例如多个线程操作同一个变量,可以用Java锁机制解决。
线程之间的死锁为了解决线程之间的安全性引入了 Java 的锁机制,而一不小心就会产生 Java 线程死锁的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。
线程太多了会将服务器资源耗尽形成死机线程数太多有可能造成系统创建大量线程而导致消耗完系统内存以及 CPU 的“过渡切换”,造成系统的死机,那么我们该如何解决这类问题呢? 某些系统资源是有限的,如文件描述符。多线程程序可能耗尽资源,因为每个 线程都可能希望有一个这样的资源。如果线程数相当大,或者某个资源的侯选线 程数远远超过了可用的资源数则最好使用资源池。一个最好的示例是数据库连接 池。只要线程需要使用一个数据库连接,它就从池中取出一个,使用以后再将它返 回池中。资源池也称为资源库。
线程的创建
线程常见实现的方式(线程池后续文章详细介绍)
- X extend Thread
- X implements Runnable
- X implements Callable
Callable与Runnable相比,call方法有返回值并支持泛型,可以抛出异常。
Thread和Runnable区别: Thread才是Java里对线程的唯一抽象,Runnable只是对任务(业务逻辑)的抽象。Thread可以接受任意一个Runnable的实例并执行。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CreateThread {
public static void main(String[] args) {
System.out.println("我在" + Thread.currentThread().getName() + "线程工作");
//1
new CallableDemo.MyThread().start();
//2
new Thread(new CallableDemo.MyRunnable()).start();
//3
CallableDemo callableDemo = new CallableDemo();
FutureTask<Integer> futureTask = new FutureTask(callableDemo);
new Thread(futureTask).start();
try {
Integer integer = futureTask.get();
System.out.println("获得返回值" + integer);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class CallableDemo implements Callable<Integer> {
private int y;
@Override
public Integer call() throws Exception {
for (int i = 0; i < 1000; i++) {
y++;
}
System.out.println("我在" + Thread.currentThread().getName() + "线程工作");
return y;
}
static class MyThread extends Thread{
/**
* 线程异步执行的真正方法
*/
@Override
public void run() {
super.run();
System.out.println("我在" + Thread.currentThread().getName() + "线程工作");
}
}
static class MyRunnable implements Runnable{
/**
* 线程异步执行的真正方法
*/
@Override
public void run() {
System.out.println("我在" + Thread.currentThread().getName() + "线程工作");
}
}
}
扩展知识
摘自《Java多线程编程实战指南》