线程安全的定义:
当多个线程访问某个类时,不管运行时环境采用何种调度方式活着这些线程如何交互执行,并且在主调用代码中不需要任何额外的同步或者协同操作,这个类都能表现出正确的行为,那么这就称这个类是线程安全的
线程安全的类中,封装了必要的同步机制,因我们的主调用代码并不需要进一步的采取同步措施
竞态条件:
由于不恰当的执行时序,而出现的不正确的结果,就是竞态条件,举个例子:a线程需要将i的原始值加一赋予i,b线程需要将a线程执行后的i再+1,赋予j,这个时候如果b线程先执行,那么只鹅个结果就是错误的,这就产生了竞态条件
加锁机制:
为了保证多线程操作中,我们需要的某些调用保持原子性,就需要在单个原子操作中更新所有相关的状态变量
java中锁加锁机制:
1)内置锁:synchronized关键字就是java中的一种内置锁
2)重入:重入锁意味着获取锁的操作的粒度是线程,而不是调用。同一个线程可以无限制的获取同一个锁,重入的实现方法是:在线程调用的时候,为每一个锁关联一个获取计数值和一个所有者线程。计数为0为时认为释放了锁或者未有线程持有锁,一个线程获取了这个锁,就计数+1,并且记录该线程
重入的典型应用就是,子类继承父类synchronized方法,这时,执行子类方法就会先获取父类上的锁,那么在super.dosomthing时,就会无法获取父类上的锁,就会产生死锁
Volatile变量:
当把一个变量声明为volatile类型后,编译和运行时都会注意到这个变量时所有线程共享的,就不会把该变量值存入寄存器中或者其他线程看不见的地方,这就意味着,你每次读区volatile变量的时候,都是读取的是内存中最新的值,如果该变量做了任何修改,同样的也会立刻写入内存中,这就保证了其他线程也能立刻发现该变量值的改变
但是它只是一种稍弱的同步机制,volatile变量只能确保可见性,并不能保证多线程调用下对volatile变量操作的原子性
线程池的使用:
1.线程池不适用于依赖性任务,对响应时间比较敏感的任务和ThreadLocal的任务
1)线程池中的线程没有执行顺序,一半谁抢到了cpu资源谁就可以执行,所以如果线程中有一个线程依赖于另一个线程的执行结果,那么不适用于线程池,如果使用了l l l777k ljoin方法,使这个线程依赖于另一个线程的结果,那么在线程池固定大小的情况下,可能会产生饥饿性死锁
2)线程执行需要时间,如果用户需要及时反馈,那也不适用于线程池
3)ThreadLocal使得每个线程都可以拥有某个变量的一个私有版本,如果线程使用中,线程大小并不固定,那么使用ThreadLocal就并不明智
4)在线程池中建议不要放置运行时间长短不一至的线程
2.设置线程池大小:
线程池的理想大小取决于任务类型和系统特性
计算型任务和IO型任务
计算密集型CPU和IO密集型CPU
3.配置ThreadPoolExecuteor
ThreadPoolExecuteor实现了Executor,这些Executor是由Executors中的工厂方法返回的,灵活稳定线程池,允许自己定制线程(newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool等)
CorePoolSize:核心线程数
MaximumPoolSize: 最大大小
keepAliveTime: 当线程数大于核心线程数时,keppAliveTime就是多余的空闲线程等待的时间,如果在此期间并无任务,则回收该线程
unit:keepAliveTime参数的时间单位
BlockingQueue<Runnable> workQueue:如果所有线程数都处于忙碌状态,那么新建立的线程会存储在队列中
原文地址:https://www.cnblogs.com/jeasGo/p/12236126.html