程序:一段静态的代码。本身不能运行也不能和数据交互。
进程:一个运行中的程序。
线程:进程中执行操作的最小单元。
线程的执行需要cpu提供资源,而cpu的分配两种方式:抢占式(java使用)和分时调度。
线程的几种状态:
线程就绪状态产生的四个原因:
1、start方法
2、阻塞事件解除
3、运行过程中调用yield,避免一个线程占用资源过多,中断一下
4、JVM从本地线程切换到其他线程,它根据自己的算法进行切换
阻塞状态产生的四个原因:
1、sleep(即:存在资源不让占用,存在异常:InterruptedException)
2、wait(即:等着其他资源执行)
3、join(即:插队线程,合并线程,待此线程执行完成之后,再执行其他线程,其他线程阻塞)
4、read、write
终止线程的两种方式:
1、线程正常执行完毕----->次数
2、外部干涉------> 加入标识
public class TerminateThread implements Runnable{
// 1、加入标识 标记线程体是否可以运行
private boolean flag = true;
private String name;
public TerminateThread(String name) {
this.name = name;
}
@Override
public void run() {
int i=0;
// 2、关联标识,true-->运行flase -->停止
while (flag){
System.out.println(name+"-->"+i++);
}
}
// 3、对外提供方法改变标识
public void terminate(){
this.flag = false;
}
public static void main(String[] args){
TerminateThread tt = new TerminateThread("C罗");
new Thread(tt).start();
for (int i=0; i < 99; i++){
if(i==88){
tt.terminate();//线程终止
System.out.println("tt game over");
}
System.out.println("main-->"+i);
}
}
}
注意:不要使用stop、destory,这两种方法不安全
守护线程(精灵线程):是为用户线程服务的,jvm停止不用等待守护线程执行完毕。可以通过一个方法(setDaemon)实现用户线程和精灵线程的转换。只要主线程操作完成,无论精灵线程的操作是否完成,都将结束。
默认情况下都是用户线程,JVM等待用户线程执行完毕才会停止
线程的优先级
优先级决定者线程可以优先抢占cpu的概率。优先级越高,越容易抢到cpu。优先级1-10。线程默认的优先级是5。通过setPriority方法来设置优先级
线程同步防止出现问题解决的方法:加队列或者锁(synchronized)
synchronized同步锁锁:锁的是对象,控制对“成员变量或类变量”对象的访问
包括两种方法:synchronized方法和synchronized块
它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
缺陷:若将一个大的方法声明为synchronized,将会大大影响效率
大致流程:
避免死锁的发生:不要在同一代码块中同时持有多个对象的锁
定时器:timer timetask
TimerTask.java:主要为任务的具体内容。
Timer.java中含有3个类:Timer、TimerThread、TaskQueue。
TaskQueue中存放一些列将要执行的TimerTask,以数组的形式存放,下标越小,则表明优先级越高(下标为0不处理,即使用的最小下标为1)。
TimerThread为Thread的扩展类,会一直从TaskQueue中获取下标为1的TimerTask进行执行。并根据该TimerTask是否需要重复执行来决定是否放回到TaskQueue中。
Timer用于配置用户期望的任务执行时间、执行次数、执行内容。它内部会配置TimerThread、TaskQueue。
具体可以参考:https://blog.csdn.net/xixi_haha123/article/details/81082321
QUARTZ:https://blog.csdn.net/noaman_wgs/article/details/80984873
Happenbefore八大原则:
单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。
锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。
volatile的happen-before原则:对一个volatile变量的写操作happen-before对此变量的任意操作(当然也包括写操作了)。
happen-before的传递性原则:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
线程启动的happen-before原则:同一个线程的start方法happen-before此线程的其它方法。
线程中断的happen-before原则:对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。
线程终结的happen-before原则:线程中的所有操作都happen-before线程的终止检测。
对象创建的happen-before原则:一个对象的初始化完成先于他的finalize方法调用。
happens-before关系如图所示:
volatile
用于保证数据的同步,也就是可见性(一个线程修改一个共享变量时,另一个线程可以读到这个修改的值,如果volatile使用恰当的话,它比synchronized的使用成本更低,因为它不会引起线程的上下文切换和调度)。
用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值
DCL单例模式:
懒汉式套路基础上加入并发控制,保证在多线程环境下,对外存在一个对象
1、构造器私有化–>避免外部new构造器
2、提供私有构造属性–>存储对象地址
3、提供公共的静态方法–>获取属性
CAS:
即比较并替换,是一种实现并发算法时常用到的技术,Java并发包中的很多类都使用了CAS技术。
CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。
缺陷:
1) CPU开销过大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很到的压力。
2) 不能保证代码块的原子性
CAS机制所保证的知识一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用synchronized了。
3) ABA问题
这是CAS机制最大的问题所在