进程和线程的区别
1.根本区别:进程是操作系统进行资源分配的最小单元,线程是操作系统进行运算调度的最小单元。
2.从属关系不同:进程中包含了线程,线程属于进程。
3.开销不同:进程的创建、销毁和切换的开销都远大于线程。
4.拥有资源不同:每个进程有自己的内存和资源,一个进程中的线程会共享这些内存和资源。
5.控制和影响能力不同:子进程无法影响父进程,而子线程可以影响父线程,如果主线程发生异常会影响其所在进程和子线程。
6.CPU利用率不同:进程的CPU利用率较低,因为上下文切换开销较大,而线程的CPU的利用率较高,上下文的切换速度快。
7.操纵者不同:进程的操纵者一般是操作系统,线程的操纵者一般是编程人员。
概念:进程可进一步细化为线程,是一个程序内部的一条执行路径
创建多线程:
1.继承Thread类创建多线程:定义一个类继承Thread类,重写run方法,创建该类的实例对象,调用start方法启动线程。
2.实现Runnable接口创建多线程:定义一个类实现runnable接口,创建该类的实例对象o,将o作为构造器参数传入Thread类实例对象,调用start方法启动线程。
3.通过Callable和Future接口创建多线程:创建callable的实现类,并实现了call方法,再创建Callable类的实例;用FutureTask类来包装Callable对象,同时也包装了它的call方法和返回值;用FutureTask的对象作为target,创建并启动;用FutureTask的get方法获得返回值。
线程的生命周期及状态转换:
新建状态:新创建了一个线程对象。
就绪状态/可运行状态:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可执行的线程池中,变得可执行,等待获取CPU的使用权。
执行状态/运行状态:就绪状态的线程获得了CPU。执行程序代码。
阻塞状态:阻塞状态的线程由于某种原因被迫放弃了CPU的使用权。临时停止执行,指导线程进入就绪状态,才有机会转到执行状态。
等待阻塞:执行的线程执行wait()方法,JVM会把该线程放到等待池中。
同步阻塞:执行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,JVM会把该线程放到等锁池中。
其他阻塞:执行的线程执行sleep()或join()方法,或者发出I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完成时,线程有一次转入就绪状态。
死亡状态:线程运行完了或者因异常退出了run()方法,该线程结束了生命周期。
线程的调度:按照特定的机制为多个线程分配CPU的使用
调度方式:
1. 分时调度模式: 是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的cpu的时间片.
2. 抢占式调度模式: JAVA虚拟机采用抢占式调度模式,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那就随机选择一个线程,使其占用CPU.处于运行状态的线程会一直运行,直至它不得不放弃CPU.
线程安全:
概念:线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
当多个线程同时共享一个全局变量,或者静态变量, 进行写的操作时, 可能会发生数据的冲突问题 ,也就是线程安全问题, 但是做读的操作不会引发线程安全问题。
解决线程安全问题 :1.使用同步机制, 使得在同一时间只能有一个线程修改共享数据 2.消除共享数据, 即多个线程数据不共享或者共享的数据不被做修改 如果使用成员变量, 对成员变量不进行修改
锁:有些业务逻辑在执行过程中要求对数据进行排他性的访问,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制。
同步锁(synchronized):
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
线程池:
什么是线程池
线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象;
为什么使用线程池
使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力;当然了.使用线程池的原因不仅仅只有这些.我们可以从线程池自身的优点上来进一步了解线程池的好处;
线程共享的内容:1.进程代码段 2.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯) 3.进程打开的文件描述符、 4.信号的处理器、 5.进程的当前目录和 6.进程用户 ID 与进程组 ID
线程独有的内容包括: 1.线程 ID 2.寄存器组的值 3.线程的堆栈 4.错误返回码 5.线程的信号屏蔽码
使用线程池有哪些优势
1:线程和任务分离,提升线程重用性;
2:控制线程并发数量,降低服务器压力,统一管理所有线程;
3:提升系统响应速度;假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;
4个参数的设计:
1:核心线程数(corePoolSize)
核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定.例如:执行一个任务需要0.1秒,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个任务,就需要10个线程,此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均,所以我们一般按照BO20原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理;
2:任务队列长度(workQueue)
任务队列长度一般设计为:核心线程数单个任务执行时间*2即可;例如上面的场景中;核心线程数设计为10.单个任务执行时间为0.1秒,则队列长度可以设计为200;
3:最大线程数(maximumPoolSize)
最大线程数的设计除了需要参照核心线程数的条件外还需要参照系统每秒产生的最大任务数决定:例如:上述环境中,如果系统每秒最大产生的任务是1000个,那么,最大线程数=(最大任务数-任务队列长度)*单个任务执行时间;既:最大线程数=(1000-200)*0.1=80个;
4:最大空闲时间(keepAliveTime)
这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值,用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可;