有关^_^多线程框架

有关_多线程框架

4.1.Java多线程基础

4.1.1 进程和多线程简介

  1. 何为进程?
    进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。
  2. 何为线程?
    **线程与进程相似,但线程是一个比进程更小的执行单位。*一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为*轻量级进程
  3. 何为多线程?
    多线程就是多个线程同时运行或交替运行。单核CPU的话是顺序执行,也就是交替运行。多核CPU的话,因为每个CPU有自己的运算器,所以在多个CPU中可以同时运行。

4.1.2 几个重要的概念

  1. 同步和异步
    同步和异步通常用来形容一次方法调用。同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者可以继续后续的操作。

关于异步目前比较经典以及常用的实现方式就是消息队列:在不使用消息队列服务器的时候,用户的请求数据直接写入数据库,在高并发的情况下数据库压力剧增,使得响应速度变慢。但是在使用消息队列之后,用户的请求数据发送给消息队列之后立即 返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度快于数据库(消息队列也比数据库有更好的伸缩性),因此响应速度得到大幅改善

  1. 并发(Concurrency)和并行(Parallelism)
    并发和并行是两个非常容易被混淆的概念。它们都可以表示两个或者多个任务一起执行,但是偏重点有些不同。并发偏重于多个任务交替执行,而多个任务之间有可能还是串行的。而并行是真正意义上的“同时执行”。并发和并行是两个非常容易被混淆的概念。它们都可以表示两个或者多个任务一起执行,但是偏重点有些不同。并发偏重于多个任务交替执行,而多个任务之间有可能还是串行的。而并行是真正意义上的“同时执行”。
    多线程在单核CPU的话是顺序执行,也就是交替运行(并发)。多核CPU的话,因为每个CPU有自己的运算器,所以在多个CPU中可以同时运行(并行)。
  2. 高并发
    高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。

高并发相关常用的一些指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等。

  1. 临界区
    临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用。但是每一次,只能有一个线程使用它,一旦临界区资源被占用,其他线程要想使用这个资源,就必须等待。在并行程序中,临界区资源是保护的对象。
  2. 阻塞和非阻塞
    非阻塞指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回,而阻塞与之相反。

4.1.3 使用多线程常见的四种方式

  1. 继承Thread类创建线程
    Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。 start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
  2. 实现Runnable接口创建线程
    如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口。
  3. **通过Callable和FutureTask创建线程 重写call方法(有返回值) **
    Java 5.0 在 java.util.concurrent 提供了一个新的创建执行线程的方式: 实现 Callable 接口。

Callable 接口类似于 Runnable,但是 Runnable 不会返回结果,并且无法抛出经过检查的异常,而 Callable 依赖 FutureTask 类获取返回结果。

public class CallableAndFuture {
    public static void main(String[] args) {
        Callable<Integer> callable = new Callable<Integer>() {
            public Integer call() throws Exception {
                return new Random().nextInt(100);
            }
        };
        FutureTask<Integer> future = new FutureTask<Integer>(callable);
        new Thread(future).start();
        try {
            Thread.sleep(5000);// 可能做一些事情
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
12345678910111213141516171819
  1. 使用线程池

    使用线程池的方式也是最推荐的一种方式,另外,《阿里巴巴Java开发手册》在第一章第六节并发处理这一部分也强调到“线程资源必须通过线程池提供,不允许在应用中自行显示创建线程”

    1. 那么既然已经可以实现多线程,那么为什么需要使用线程池呢?
      虽然以上两种方式能够实现多线程,但是如果不加控制,反而会对系统造成负担。线程本身也要占用内存空间,因为jvm会为每个线程分配一定内存(JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K,也可以通过jvm参数-Xss来设置),所以当线程数达到一定数量时就报了该error。大量的线程会占用内存资源并且可能会导致Out of Memory。即便没有这样的情况,大量的线程回收也会给GC带来很大的压力。
      现在问题明朗了:java线程池是为了防止内存溢出,而不是为了加快效率。

    2. 线程池的作用
      线程池的作用就2个:

      1. **减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务**
      
      2. 可以根据系统的承受能力,调整线程池中工作线程的数据,防止因为消耗过多的内存导致服务器崩溃。
      123
      
    3. 线程池类结构
      在这里插入图片描述
      这张图基本简单代表了线程池类的结构

1、最顶级的接口是Executor,不过Executor严格意义上来说并不是一个线程池而只是提供了一种任务如何运行的机制而已

2、ExecutorService才可以认为是真正的线程池接口,接口提供了管理线程池的方法

3、下面两个分支,AbstractExecutorService分支就是普通的线程池分支,ScheduledExecutorService是用来创建定时任务的。
Executors
java.util.concurrent.Executors工厂类可以创建四种类型的线程池,通过Executors.newXXX方法即可创建。

下面来看一下Executors为用户提供的几种线程池:(重点)
1、newSingleThreadExecutos() 单线程线程池, 它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级列队)执行。
\2. newFixedThreadPool(int nThreads) 固定大小线程池 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
\3. newCachedThreadPool 缓存线程池 创建一个可缓存线程池,这种线程池内部没有核心线程,线程的数量是有没限制的。(闲置状态)在超过了60S还不做事,就会销毁
\4. newScheduledThreadPool 定时线程池,创建一个定长线程池,支持定时及周期性任务执行。

4.1.4 一些常用方法

  1. currentThread():返回对当前正在执行的线程对象的引用
  2. getId():返回此线程的标识符
  3. getName():返回此线程的名称
  4. setName(String name):将此线程的名称更改为等于参数 name 。
  5. getPriority():返回此线程的优先级
  6. isAlive():测试这个线程是否还处于活动状态。
    什么是活动状态呢?
    活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备运行的状态。
  7. sleep(long millis):使当前正在执行的线程以指定的毫秒数“休眠”(暂时停止执行)
  8. isDaemon():测试这个线程是否是守护线程
  9. setDaemon(boolean on):将此线程标记为 daemon线程或用户线程。
  10. join():在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是 主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
  11. yield():yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU时间。注意:放弃的时间不确定,可能一会就会重新获得CPU时间片。
  12. setPriority(int newPriority):更改此线程的优先级
  13. interrupt():中断这个线程。
  14. interrupted() 和isInterrupted()
    interrupted(): 测试当前线程是否已经是中断状态,执行后具有将状态标志清除为false的功能

isInterrupted(): 测试线程Thread对相关是否已经是中断状态,但部清楚状态标志

4.1.5 线程的优先级与守护线程

线程的优先级

  1. 线程优先级具有继承特性比如A线程启动B线程,则B线程的优先级和A是一样的。
  2. 线程优先级具有随机性也就是说线程优先级高的不一定每一次都先执行完。
  3. 在默认情况下优先级都是Thread.NORM_PRIORITY(常数5)

守护线程

  1. 用户线程: 运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
  2. 守护线程: 运行在后台,为其他前台线程服务.也可以说守护线程是JVM中非守护线程的 “佣人”。
  3. 特点: 一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作
  • 运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
  1. 守护线程: 运行在后台,为其他前台线程服务.也可以说守护线程是JVM中非守护线程的 “佣人”。
  2. 特点: 一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作
  3. 应用: 数据库连接池中的检测线程,JVM虚拟机启动后的检测线程
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值