对进程、线程、多线程、线程池的理解

一、进程与线程

进程是指内存中运行的应用程序,每个进程都有自己独立的一块内存空间。
线程是指进程中的一个执行流程,一个进程中可以运行多个线程。
进程和线程的区别:

  1. 线程是进程内的一个执行单元,进程至少有一个线程,多线程共享进程的地址空间,而进程有自己独立的地址空间。
  2. 操作系统以进程为单位分配资源,同一个进程内的线程,共享进程的资源。
  3. 线程是处理器调度的基本单位,但进程不是。
1.1线程的生命周期:
  1. 新建状态(New): 新创建一个线程对象。
  2. 就绪状态(Runnable): 线程对象创建后,其他线程嗲用了该对象的start()方法,该状态的线程位于“可运行线程池”中,变得可运行,只等待获取cpu的使用权,即在就绪状态的进程除cpu之外,其它的运行所需要资源都已全部获得。
  3. 运行状态(Running): 就绪状态的线程获取了cpu,执行程序代码。
  4. 阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃cpu使用权,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态。
    阻塞状态分为三种:
    A:等待阻塞–运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒。
    B:同步阻塞–运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。
    C:其他阻塞–运行的线程执行sleep()或join()方法,或者发出 I/O 请求时,JVM会把线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时,或者 I/O 处理完毕时,线程重新转入就绪状态。
  5. 死亡状态(Dead): 线程执行了完成或者因异常退出run()方法,该线程结束生命周期。
1.2 状态转换图如下:

在这里插入图片描述

1.3、wait和sleep的区别
  1. sleep是Thread类的方法,是线程用来控制自身流程的,调用sleep(),可以设置睡眠时间,在设定的时间后会继续执行。
  2. wait是Object类的方法,用来线程间的通信,这个方法会使当前拥有该对象锁的进程等待知道其他线程调用notify()或者notigyAll()唤醒wait等待的线程.
  3. 调用sleep方法不会释放锁(自己的感觉是sleep方法本来就是和锁没有关系的,因为他是一个线程用于管理自己的方法,不涉及线程通信)
  4. 最大的不同是在等待时 wait 会释放锁,而 sleep 一直持有锁。 wait 通常被用于线程间交互, sleep 通常被用于暂停执行。
1.4、sleep和yield的区别
  1. sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给优先级相同,或优先级更高的线程执行机会。
  2. sleep()方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态;而yield()方法不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。因此完全有可能某个线程调用yield()方法暂停之后,立即再次获得处理器资源被执行。
  3. sleep()方法声明抛出了InterruptedException异常,所以调用sleep()方法时要么捕捉该异常,要么显式声明抛出该异常;而yield()方法则没有声明抛出任何异常。
  4. sleep()方法比yield()方法有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行。
二、多线程
2.1什么是多线程?

多线程就是指一个进程中同时有多个执行路径(线程)正在执行。

2.2为什么要使用多线程?
  1. 在一个程序中,有很多的操作是非常耗时的,如数据库读写操作,IO操作等,如果使用单线程,那么程序就必须等待这些操作执行完成之后才能执行其他操作。使用多线程,可以在将耗时任务放在后台继续执行的同时,同时执行其他操作。

  2. 可以提高程序的效率。

  3. 在一些等待的任务上,如用户输入,文件读取等,多线程就非常有用了。

2.3多线程的实现方式:
  1. 继承 Thread 类: 但 Thread 本质上也是实现了 Runnable 接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过 Thread 类的 start()实例方法。 start()方法是一个native 方法,它将启动一个新线程,并执行 run()方法。这种方式实现多线程很简单,通过自己的类直接 extend Thread,并复写 run()方法,就可以启动新线程并执行自己定义的 run()方法。例如: 继承 Thread 类实现多线程,并在合适的地方启动线程
1. public class MyThread extends Thread {
2. public void run() {
3. System.out.println("MyThread.run()");
4.	 }
5. }
6. MyThread myThread1 = new MyThread();
7. MyThread myThread2 = new MyThread();
8. myThread1.start();
9. myThread2.start();
  1. 实现 Runnable 接口的方式实现多线程,并且实例化 Thread,传入自己的 Thread 实例,调用 run( )方法
1. public class MyThread implements Runnable {
2. public void run() {
3. System.out.println("MyThread.run()");
4.	 }
5. }
6. MyThread myThread = new MyThread();
7. Thread thread = new Thread(myThread);
8. thread.start();
  1. 通过Callable和Future创建有返回值的多线程
    首先定义一个类实现Callable接口,重写call()方法,然后在启动类中new一个FutureTask,将实现Callable接口的实现类的实例放进去,然后new一个Thread,将FutureTask实例放进去,调用start()方法启动。
有返回值的多线程案例:

有返回值的任务必须实现 Callable 接口,类似的,无返回值的任务必须 Runnable 接口。执行 Callable 任务后,可以获取一个 Future 的对象,在该对象上调用 get 就可以获取到 Callable 任务返回的 Object 了,再结合线程池接口ExecutorService 就可以实现传说中有返回结果的多线程了。

1. import java.util.concurrent.*;
2. import java.util.Date;
3. import java.util.List;
4. import java.util.ArrayList;
5.
6. /**
7. * 有返回值的线程
8. */
9. @SuppressWarnings("unchecked")
10. public class Test {
11. public static void main(String[] args) throws ExecutionException,
12. InterruptedException {
13. System.out.println("----程序开始运行----");
14. Date date1 = new Date();
15.
16. int taskSize = 5;
17. // 创建一个线程池
18. ExecutorService pool = Executors.newFixedThreadPool(taskSize);
19. // 创建多个有返回值的任务
20. List<Future> list = new ArrayList<Future>();
21. for (int i = 0; i < taskSize; i++) {
22. Callable c = new MyCallable(i + " ");
23. // 执行任务并获取 Future 对象
24. Future f = pool.submit(c);
25. // System.out.println(">>>" + f.get().toString());
26. list.add(f);
27. }
28. // 关闭线程池
29. pool.shutdown();
30.
31. // 获取所有并发任务的运行结果
32. for (Future f : list) {
33. // 从 Future 对象上获取任务的返回值,并输出到控制台
34. System.out.println(">>>" + f.get().toString());
35. }
36.
37. Date date2 = new Date();
38. System.out.println("----程序结束运行----,程序运行时间【"
39. + (date2.getTime() - date1.getTime()) + "毫秒】 ");
40. }
41. }
42.
43. class MyCallable implements Callable<Object> {
44. private String taskNum;
45.
46. MyCallable(String taskNum) {
47. this.taskNum = taskNum;
48. }
49.
50. public Object call() throws Exception {
51. System.out.println(">>>" + taskNum + "任务启动");
52. Date dateTmp1 = new Date();
53. Thread.sleep(1000);
54. Date dateTmp2 = new Date();
55. long time = dateTmp2.getTime() - dateTmp1.getTime();
56. System.out.println(">>>" + taskNum + "任务终止");
57. return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】 ";
58. }
59. }
三、线程池
3.1什么是线程池?

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。我们知道线程的创建和销毁相对于线程的执行来说是比较消耗资源的,我们可以用线程池缓存线程,用已有的闲置线程来执行新任务。也可以避免线程并发数量过多,抢占系统资源从而导致阻塞。

3.2在Java中,线程池有一个最大的父接口Executor,而它的具体实现为ThreadPoolExecutor类,ThreadPoolExecutor类中提供了四种构造函数,这些构造函数里有这些参数:

corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:该线程池中非核心线程闲置超时时长 TimeUnit:keepAliveTime的单位
BlockingQueue:该线程池中的任务队列:维护着等待执行的Runnable对象
ThreadFactory:线程工厂,工作线程创建时使用的工厂,new他的时候需要实现他的Thread newThread(Runnable)方法。
RejectedExecutionHandler:拒绝策略,是一个回调函数,当线程池中队列已满而且线程数也已经达到maximumPoolSize时,线程池就会拒绝接收任务并且调用这个回调函数,
通过ThreadPoolExecutor.execute(Runnable command)方法即可向线程池内添加任务。
如果不想自己创建线程池的话,也可以使用Executors,Java通过Executors提供了四种线程池,这四种线程池都是直接或间接配置ThreadPoolExecutor的参数实现的,
newCachedThreadPool() 可缓存线程池: 线程数无限制,有空闲线程则复用空闲线程,若无空闲线程则新建线程
newFixedThreadPool() 定长线程池:,可控制线程最大并发数(同时执行的线程数) 超出的线程会在队列中等待
newScheduledThreadPool() 定长线程池: 支持定时及周期性任务执行。
newSingleThreadExecutor() 单线程化的线程池: 有且仅有一个工作线程执行任务,所有任务按照指定顺序执行,即遵循队列的入队出队规则

创建方法:ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

3.3对线程池的理解??

①:如何使用?

线程池是多线程的一种处理形式,它事先把多个线程对象放在一个容器当中,使用时不用new线程,而是直接去线程池中去拿,节省了开辟子线程空间,降低线程多引起的并发。

②:好处:

降低资源消耗、提高响应速度(不需要创建线程就执行)、提高线程可管理性—>线程池可统一分配、调优和监控。

③:启动策略:

  1. 线程池刚创建时,里面没有一个线程,任务队列是作为参数传递进来的。
  2. 调用execute()方法,添加一个任务,线程池会对其进行判断:< 以corePoolSize为界>
    A:当前运行线程数小于corePoolSize,会立马创建线程执行任务。
    B:大于或等于corePoolSize,将任务放入队列。
    C:队列满了,并且正在运行线程数小于 maximumPoolSize,继续创建线程进行任务,如果大于或等于maximumPoolSize,抛异常不再接受任务。
  3. 完成任务时,会从队列中取出下一个任务执行。
  4. 当线程无事可做,超过一定时间,线程池会对其进行判断,如果当前运行线程大于corePoolSize,线程就被停掉,任务完成后,收缩到corePoolSize大小。
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值