线程

线程是什么?

1.线程是CPU调度的最小单位.由于CPU计算指令可以达到数十亿次/秒.因此表现出来的就是线程在并发执行.

2.通常的线程的调度方式有:分时调度和抢占式调度.目前大部分都是抢占式调度.

3.java中的线程调度是交给了操作系统来执行的.因此java中的线程也有优先级.1-10,数量越大,优先级越高,默认值为5.需要注意的是,并不是优先级越大,肯定会先执行,只能说大概率会先执行.

线程的创建方式

1.继承Thread

2.实现runable接口

3.通过callable和FutureTask创建有返回值的线程.

查看UML图:

可以看到,Thread包含Runable属性,中间用一个RunableFuture接口是去继承Runable接口和Future接口.然后FutureTask去实现RunableFuture接口,从而可以让FutureTask能够作为Thread的构造参数.Callable又是FutureTask的成员属性,最终call执行结果保存在FutureTask中,通过Future的get()获取执行结果.

4.通过线程池.

线程的生命周期

1.NEW状态

新建一个线程,但是还没有start的时候,线程的默认状态.

2.RUNABLE状态

调用start()方法后的状态.因为java的线程管理是有操作系统管理的,操作系统中包含就绪状态和运行状态.当调用start()方法后,在操作系统中首先是就绪状态,但是要等到CPU时间片来执行,然后变成运行状态,如果在一个时间片没有执行完,会变回就绪状态,然后等待下次的时间片,如此反复,直到程序执行完或者异常退出.

3.TERMAINTED状态

线程执行完或者异常退出的状态.

4.TIME_WAITING状态

限时等待状态.

能让线程进入此状态的方法通常有:

a.thread.join()方法

b.object.wait()方法.

c.Thread.sleep()方法.

5.BLOCKING状态

a.等待获取锁的时候,进入堵塞状态

b.io堵塞,比如:等待用户的输入

线程的操作

sleep()

线程进入TIME_WAITING状态

interrupt()

将线程设置为中断状态.这个命令的执行有2个作用:

a.会让处于TIME_WAITING状态的线程报异常退出

b.给线程设置中断状态标记,从而使开发者能够根据这个状态做出相应的处理.

join()

假如A线程的执行需要B线程,就可以使用此方法.

在A线程的run()执行:threadB.join();

yiead()

让出当前cpu时间片,重新调度.

deamon()

守护线程,和jvm进程绑定.

线程池技术

线程池调度流程

使用线程池优势

1.降低资源消耗,线程是稀缺资源,并且在新建和销毁都会占用不少资源.

2.提高响应速度.因为可以重用已经创建好的线程.

3.进行线程监控.

实例:

使用:

拒绝策略:

在例子中我们虽重写了拒绝策略,但是也只是增加了一些日志,返回了拒绝报异常策略.

jdk提供的拒绝策略有:

AbortPolicy:默认的策略,抛异常.

CallerRunsPolicy:让提交任务的当前线程执行.

DiscardPolicy:丢弃,不做任何处理和通知.静默处理.

DiscardOldestPolicy:从队列里面poll最先加入的线程,丢弃最老的策略.

线程数的确定

IO密集型任务

线程数:2n.

Runtime.getRuntime().availableProcessors()*2;

CPU密集型任务

n

Runtime.getRuntime().availableProcessors();

ThreadLocal

简介

本地线程变量,是线程隔离的.一种无锁的解决线程安全问题.

使用场景

线程隔离

常用于一个用户的会话信息,数据库连接,http请求.Session管理

跨函数传递数据

源码分析

thread包含threadLocalMap

threadLocalMap是一种简易版的hashmap结构

需要注意的是,Entry是一种弱引用,好处是防止内存泄露.

当方法执行完后,方法中的threadLocal被销毁,但是Entry中的threadLocal还存在,如果不是弱引用,将导致内存溢出.

threadLocal使用原则

1.使用private static final修饰ThreadLocal

private和final防止被修改.

static可以保证全局唯一.

2.threadLocal使用完后务必手动调用remove方法.可以简单有效的避免内存溢出.

Last updated