一、线程的介绍

1.1、概念

进程:

  • 你的硬盘上有一个简单的程序,这个程序叫QQ.exe,这是一个程序,这个程序是一个静态的概念,它被扔在硬盘上也没人理他,但是当你双击它,弹出一个界面输入账号密码登录进去了,OK,这个时候叫做一个进程。进程相对于程序来说它是一个动态的概念。

线程:

  • 作为一个进程里面最小的执行单元它就叫一个线程,用简单的话讲一个程序里不同的执行路径就叫做一个线程。一个进程中可以包含多个线程。

单线程:

  • 多个任务依次执行,效率低。

多线程:

  • 多个任务同时执行,往往会有线程安全的问题。

1.2、线程调度方式

分时调度:

  • 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

抢占式调度:

  • 多个线程之间竞争,优先级高的线程竞争优势大,Java 采用这种模式。
  • 线程优先级为 1-10,其中 1 最低,10 最高。主线程的优先级默认为 5,子线程的优先级默认和主线程的优先级一至。
  • 主线程就是 main 方法,从 main 方法开始,一直到 main 方法执行结束。

二、线程的创建及常用方法

2.1 创建线程的几种方式

  • 继承 Thread 类,重写 run 方法,对象,调用 start 方法开启线程。
  • 实现 Runnable 接口,重写 run 方法,封装 Thread 对象,调用 start 方法开启线程。
  • 实现 Callable 接口,重写 call方法,封装 Thread 对象,调用 start 方法开启线程。
    • 与前两种的区别是:存在返回值,并且会抛出异常。
  • 通过线程池获得(后文具体介绍)。

举例如下:

import java.util.concurrent.*;

/**
* @author xiandongxie
*/
public class ThreadDemo { public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getPriority() + "\t" + System.currentTimeMillis()); // 线程一
new ThreadDemo()
.new Thread1()
.start(); // 线程二
new Thread(new ThreadDemo().new Thread2())
.start(); // 线程三
Thread3 thread3 = new ThreadDemo().new Thread3();
FutureTask<String> futureTask = new FutureTask<>(thread3);
Thread thread = new Thread(futureTask);
thread.start();
// 这是一个阻塞方法,只有拿到 call 的返回值后,才会继续执行
// String name = futureTask.get();
// 同样是一个阻塞方法,不过可以设置等待时间
String name = null;
try {
name = futureTask.get(2000, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
e.printStackTrace();
}
System.out.println("futureTask.get() = " + name); System.out.println("main end " + System.currentTimeMillis());
} class Thread1 extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getPriority() + "\t" + System.currentTimeMillis());
}
} class Thread2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getPriority() + "\t" + System.currentTimeMillis());
}
} class Thread3 implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(5000);
System.out.println("call 方法执行完毕...\t" + Thread.currentThread().getPriority() + "\t" + System.currentTimeMillis());
String name = Thread.currentThread().getName();
return name;
}
}
}

2.2 线程的状态



2.3 线程常用的方法

  • run,线程要执行的逻辑代码。
  • start,开启一个线程。
  • Sleep,意思就是睡眠,当前线程暂停一段时间让给别的线程去运行。Sleep是怎么复活的?由你的睡眠时间而定,等睡眠到规定的时间自动复活。sleep 不会释放锁,只是释放 CPU 执行资源
  • Yield,就是当前线程正在执行的时候停止下来进入等待队列,回到等待队列里在系统的调度算法里头呢还是依然有可能把你刚回去的这个线程拿回来继续执行,当然,更大的可能性是把原来等待的那些拿出一个来执行,所以yield的意思是我让出一下CPU,后面你们能不能抢到那我不管。
  • join, 意思就是在自己当前线程加入你调用Join的线程(),本线程等待。等调用的线程运行完了,自己再去执行。t1和t2两个线程,在t1的某个点上调用了t2.join,它会跑到t2去运行,t1等待t2运行完毕继续t1运行(自己join自己没有意义)。
  • wait,是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)
  • wait(long timeout),让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的notify()方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)
  • notify 和 notifyAll ,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。

三、线程池

通过建立池可以有效的利用系统资源,节约系统性能。Java 中的线程池就是一种非常好的实现,从 JDK 1.5 开始 Java 提供了一个线程工厂 Executors 用来生成线程池,通过 Executors 可以方便的生成不同类型的线程池。

线程池的优点:

  • 降低资源消耗。线程的开启和销毁会消耗资源,通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池的使用:

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; /**
* @author xiandongxie
*/
public class ThreadPool { //参数初始化 返回Java虚拟机可用的处理器数量
// private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CPU_COUNT = 2;
//核心线程数量大小
private static final int corePoolSize = Math.max(2, Math.min(CPU_COUNT - 1, 4));
//线程池最大容纳线程数
private static final int maximumPoolSize = CPU_COUNT * 2 + 1;
//线程空闲后的存活时长
private static final int keepAliveTime = 30; //任务过多后,存储任务的一个阻塞队列
BlockingQueue<Runnable> workQueue = new SynchronousQueue<>(); //线程的创建工厂
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) {
return new Thread(r, "AdvacnedAsyncTask #" + mCount.getAndIncrement());
}
}; //线程池任务满载后采取的任务拒绝策略: 不执行新任务,直接抛出异常,提示线程池已满
RejectedExecutionHandler rejectHandler = new ThreadPoolExecutor.AbortPolicy(); //线程池对象,创建线程
ThreadPoolExecutor mExecute = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
threadFactory,
rejectHandler
); public static void main(String[] args) {
System.out.println("main start ..... \nCPU_COUNT = " + CPU_COUNT + "\tcorePoolSize=" + corePoolSize + "\tmaximumPoolSize=" + maximumPoolSize); ThreadPool threadPool = new ThreadPool();
ThreadPoolExecutor execute = threadPool.mExecute;
// 预启动所有核心线程
execute.prestartAllCoreThreads(); for (int i = 0; i < 5; i++) {
execute.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "\tstart..." + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\tend..." + System.currentTimeMillis());
}
});
}
execute.shutdown(); System.out.println("main end .....");
}
}

参数介绍:

  • corePoolSize

    • 线程池的核心线程数。在没有设置 allowCoreThreadTimeOut 为 true 的情况下,核心线程会在线程池中一直存活,即使处于闲置状态。
  • maximumPoolSize
    • 线程池所能容纳的最大线程数。当活动线程(核心线程+非核心线程)达到这个数值后,后续任务将会根据 RejectedExecutionHandler 来进行拒绝策略处理。
  • keepAliveTime
    • 非核心线程闲置时的超时时长。超过该时长,非核心线程就会被回收。若线程池通过 allowCoreThreadTimeOut() 方法设置 allowCoreThreadTimeOut 属性为 true,则该时长同样会作用于核心线程,AsyncTask 配置的线程池就是这样设置的。
  • unit
    • keepAliveTime 时长对应的单位。
  • workQueue
    • 线程池中的任务队列,通过线程池的 execute() 方法提交的 Runnable 对象会存储在该队列中。
    • 是一个阻塞队列 BlockingQueue,虽然它是 Queue 的子接口,但是它的主要作用并不是容器,而是作为线程同步的工具,他有一个特征,当生产者试图向 BlockingQueue 放入(put)元素,如果队列已满,则该线程被阻塞;当消费者试图从 BlockingQueue 取出(take)元素,如果队列已空,则该线程被阻塞。
  • ThreadFactory
    • 线程工厂,功能很简单,就是为线程池提供创建新线程的功能。这是一个接口,可以通过自定义,做一些自定义线程名的操作。
  • RejectedExecutionHandler
    • 当任务无法被执行时(超过线程最大容量 maximum 并且 workQueue 已经被排满了)的处理策略,这里有四种任务拒绝类型:

      1. AbortPolicy: 不执行新任务,直接抛出异常,提示线程池已满,涉及到该异常的任务也不会被执行,线程池默认的拒绝策略就是该策略。
      2. DisCardPolicy: 不执行新任务,也不抛出异常;
      3. DisCardOldSetPolicy: 将消息队列中的第一个任务(即等待时间最久的任务)替换为当前新进来的任务执行;
      4. CallerRunsPolicy: 直接调用execute来执行当前任务;

线程池工作原理:

常见的线程池:

  • CachedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。
  • SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。
  • SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景。
  • FixedThreadPool:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程

一、线程 & 线程池的更多相关文章

  1. GIL 线程/进程池 同步异步

    GIL 什么是GIL 全局解释器锁,本质是一把互斥锁,是加在cpython解释器上的一把锁, 同一个进程内的所有线程需要先抢到GIL锁,才能执行python代码 为什么要有GIL cpython解释器 ...

  2. 常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq

    常量,字段,构造方法   常量 1.什么是常量 ​ 常量是值从不变化的符号,在编译之前值就必须确定.编译后,常量值会保存到程序集元数据中.所以,常量必须是编译器识别的基元类型的常量,如:Boolean ...

  3. 子进程回收资源两种方式,僵尸进程与孤儿进程,守护进程,进程间数据隔离,进程互斥锁,队列,IPC机制,线程,守护线程,线程池,回调函数add_done_callback,TCP服务端实现并发

    子进程回收资源两种方式 - 1) join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源. - 2) 主进程 “正常结束” ,子进程与主进程一并被回收资源. from multipr ...

  4. C#线程 线程进阶

    第四部分:高级线程 非阻塞同步 前面我们说过,即使在分配或增加字段的简单情况下,也需要同步.尽管锁定始终可以满足此需求,但是竞争性锁定意味着线程必须阻塞,从而遭受上下文切换的开销和调度的延迟,这在高度 ...

  5. Linux线程的实现 & LinuxThread vs. NPTL & 用户级内核级线程 & 线程与信号处理

    另,线程的资源占用可见:http://www.cnblogs.com/charlesblc/p/6242111.html 进程 & 线程的很多知识可以看这里:http://www.cnblog ...

  6. Linux线程 之 线程 线程组 进程 轻量级进程(LWP)

    Thread Local Storage,线程本地存储,大神Ulrich Drepper有篇PDF文档是讲TLS的,我曾经努力过三次尝试搞清楚TLS的原理,均没有彻底搞清楚.这一次是第三次,我沉浸gl ...

  7. JAVA之旅(十五)——多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止

    JAVA之旅(十五)--多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止 我们接着多线程讲 一.生产者和消费者 什么是生产者和消费者?我们 ...

  8. python 进程和线程-线程和线程变量ThreadLocal

    线程 线程是由若干个进程组成的,所以一个进程至少包含一个线程:并且线程是操作系统直接支持的执行单元.多任务可以由多进程完成,也可由一个进程的多个线程来完成 Python的线程是真正的Posix Thr ...

  9. 进程?线程?多线程?同步?异步?守护线程?非守护线程(用户线程)?线程的几种状态?多线程中的方法join()?

    1.进程?线程?多线程? 进程就是正在运行的程序,他是线程的集合. 线程是正在独立运行的一条执行路径. 多线程是为了提高程序的执行效率.2.同步?异步? 同步: 单线程 异步: 多线程 3.守护线程? ...

  10. 关于Spring事务的原理,以及在事务内开启线程,连接池耗尽问题.

    主要以结果为导向解释Spring 事务原理,连接池的消耗,以及事务内开启事务线程要注意的问题. Spring 事务原理这里不多说,网上一搜一大堆,也就是基于AOP配合ThreadLocal实现. 这里 ...

随机推荐

  1. Jmeter与LoadRunner的比较

    一.与Loadrunner的比较相似点 1.Jmeter的架构跟loadrunner原理一样, 都是通过中间代理,监控&收集并发客户端发现的指令,把他们生成脚本,再发送到应用服务器,再监控服务 ...

  2. 用scrapy实现模拟登陆

    class Test1sSpider(scrapy.Spider): name = 'test1s' allowed_domains = ['yaozh.com'] start_urls = ['ht ...

  3. Linux 日常操作

    Linux 日常操作 */--> Linux 日常操作 Table of Contents 1. 查看硬件信息 1.1. 服务器型号序列号 1.2. 主板型号 1.3. 查看BIOS信息 1.4 ...

  4. linux--配置开发环境 --Apache篇

    现在我的的linux服务器上一般都是使用:Apache 和  Nginx 这两种配置. 你现在安装好了,启动了,也无法通过你服务器绑定的网址访问你的网站. 这是你可以通过这个命令查看一下你的80端口: ...

  5. Test Test...

    标题: Test(一级标题) Test(二级标题) Test(三级标题) 列表: test(列表) Alpha Beta Gamma test 2 Delte Epsilon 链接: 点兔成金斐波那契 ...

  6. vue + ArcGIS 地图应用系列一:arcgis api本地部署(开发环境)

    封面 1. 下载 ArcGIS API for JavaScript 官网地址: https://developers.arcgis.com/javascript/3/ 下载地址:http://lin ...

  7. 计算5的n次幂html代码

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. Linux系统应用管理:增加普通用户(密码管理等)

    1. 查看当前Linux系统的版本.内核等信息 [root@oldboy ~]# cat /etc/redhat-release CentOS release 6.7 (Final) . # 系统版本 ...

  9. Linux系统介绍与环境搭建准备

    1 什么是操作系统? 操作系统,Operating System,简称OS,是计算机系统中必不可少的基础软件,它是应用程序运行以及用户操作必备的基础环境支撑,是计算机系统的核心.   操作系统的作用是 ...

  10. Blog Customization

    0 前言 从大二开始写博客,主要为了记录自己学习过程中的问题.尝试使用过CSDN.博客园等公共服务,也用Github pages搭建过自己的博客,但效果都不令人满意.CSDN广告太多,界面乌烟瘴气,而 ...