线程是进程中独立运行的子任务。java
方式一:将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法
方式二:声明实现 Runnable 接口的类。该类而后实现 run 方法
推荐方式二,由于接口方式比继承方式更灵活,也减小程序间的耦合。api
Thread.currentThread()数组
线程分为守护线程、用户线程。线程初始化默认为用户线程。
setDaemon(true) 将该线程标记为守护线程或用户线程。
特性:设置守护线程,会做为进程的守护者,若是进程内没有其余非守护线程,那么守护线程也会被销毁,即便可能线程内没有运行结束。缓存
某线程a 中启动另一个线程t,那么咱们称线程t是线程a的一个子线程,而 线程a是线程t的父线程。安全
最典型的就是咱们在main方法中 启动一个线程去执行。其中main方法隐含的main线程为父线程。多线程
(1)start() 使线程处于就绪状态,Java虚拟机会调用该线程的run方法;
(2)stop() 中止线程,已过期,存在不安全性:
一是可能请理性的工做得不得完成;
二是可能对锁定的对象进行“解锁”,致使数据不一样步不一致的状况。
推荐 使用 interrupt() +抛异常 中断线程。
(3)suspend() 暂停线程,已过期。
resume() 恢复线程,已过期。
suspend 与resume 不建议使用,存在缺陷:
一是可能独占同步对象;
二是致使数据不一致。
(4)yield() 放弃当前线程的CPU资源。放弃时间不确认,也有可能刚刚放弃又得到CPU资源。
(5)t.join() 等待该线程t 销毁终止。并发
一 原子性(互斥性):实现多线程的同步机制,使得锁内代码的运行必需先得到对应的锁,运行完后自动释放对应的锁。
二 内存可见性:在同一锁状况下,synchronized锁内代码保证变量的可见性。
三 可重入性:当一个线程获取一个对象的锁,再次请求该对象的锁时是能够再次获取该对象的锁的。
若是在synchronized锁内发生异常,锁会被释放。框架
总结:dom
(1)synchronized方法 与 synchronized(this) 代码块 锁定的都是当前对象,不一样的只是同步代码的范围异步
(2)synchronized (非this对象x) 将对象x自己做为“对象监视器”:
a、多个线程同时执行 synchronized(x) 代码块,呈现同步效果。
b、当其余线程同时执行对象x里面的 synchronized方法时,呈现同步效果。
c、当其余线程同时执行对象x里面的 synchronized(this)方法时,呈现同步效果。
(3)静态synchronized方法 与 synchronized(calss)代码块 锁定的都是Class锁。Class 锁与 对象锁 不是同一个锁,二者同时使用状况可能呈异步效果。
(4)尽可能不使用 synchronized(string),是由于string的实际锁为string的常量池对象,多个值相同的string对象可能持有同一个锁。
一 内存可见性:保证变量的可见性,线程在每次使用变量的时候,都会读取变量修改后的最的值。
二 不保证原子性。
线程间通讯的方式主要为共享内存、线程同步。
线程同步除了synchronized互斥同步外,也可使用wait/notify实现等待、通知的机制。
(1)wait/notify属于Object类的方法,但wait和notify方法调用,必须获取对象的对象级别锁,即synchronized同步方法或同步块中使用。
(2)wait()方法:在其余线程调用此对象的 notify() 方法或 notifyAll() 方法前,或者其余某个线程中断当前线程,致使当前线程一直阻塞等待。等同wait(0)方法。
wait(long timeout) 在其余线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其余某个线程中断当前线程,或者已超过某个实际时间量前,致使当前线程等待。 单位为毫秒。
void wait(long timeout, int nanos) 与 wait(long timeout) 不一样的是增长了额外的纳秒级别,更精细的等待时间控制。
(3)notfiy方法:唤醒在此对象监视器上等待的单个线程。选择是任意性的,并在对实现作出决定时发生。线程经过调用其中一个 wait 方法,在对象的监视器上等待。
(4)notifyAll方法:唤醒在此对象监视器上等待的全部线程。
须要:wait被执行后,会自动释放锁,而notify被执行后,锁没有马上释放,由synchronized同步块结束时释放。
应用场景:简单的生产、消费问题。
synchronized (lock) {//获取到对象锁lock try { lock.wait();//等待通讯信号, 释放对象锁lock } catch (InterruptedException e) { e.printStackTrace(); } //接到通讯信号} synchronized (lock) {//获取到对象锁lock lock.notify();//通知并唤醒某个正等待的线程 //其余操做}//释放对象锁lock
让每一个线程都有本身独立的共享变量,有两种方式:
一 该实例变量封存在线程类内部;若是该实例变量(非static)是引用类型,存在可能逸出的状况。
二 就是使用ThreadLocal在任意地方构建变量,即便是静态的(static)。具备很好的隔离性。
(1)重写initialValue()方法: 初始化ThreadLocal变量,解决get()返回null问题
(2)InheritableThreadLocal 子线程能够读取父线程的值,但反之不行
一个简单的示例:
private java.util.concurrent.locks.Lock lock = new ReentrantLock(); public void method() { try { lock.lock(); //获取到锁lock,同步块 } finally { lock.unlock();//释放锁lock } }
ReentrantLock 比 synchronized 功能更强大,主要体现:
(1)ReentrantLock 具备公平策略的选择。
(2)ReentrantLock 能够在获取锁的时候,可有条件性地获取,能够设置等待时间,颇有效地避免死锁。
如 tryLock() 和 tryLock(long timeout, TimeUnit unit)
(3)ReentrantLock 能够获取锁的各类信息,用于监控锁的各类状态。
(4)ReentrantLock 能够灵活实现多路通知,即Condition的运用。
1、公平锁与非公平锁
ReentrantLock 默认是非公平锁,容许线程“抢占插队”获取锁。公平锁则是线程依照请求的顺序获取锁,近似FIFO的策略方式。
2、锁的使用:
(1)lock() 阻塞式地获取锁,只有在获取到锁后才处理interrupt信息
(2)lockInterruptibly() 阻塞式地获取锁,当即处理interrupt信息,并抛出异常
(3)tryLock() 尝试获取锁,无论成功失败,都当即返回true、false,注意的是即便已将此锁设置为使用公平排序策略,tryLock()仍然能够打开公平性去插队抢占。若是但愿遵照此锁的公平设置,则使用 tryLock(0, TimeUnit.SECONDS),它几乎是等效的(也检测中断)。
(4)tryLock(long timeout, TimeUnit unit)在timeout时间内阻塞式地获取锁,成功返回true,超时返回false,同时当即处理interrupt信息,并抛出异常。
若是想使用一个容许闯入公平锁的定时 tryLock,那么能够将定时形式和不定时形式组合在一块儿:
if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
private java.util.concurrent.locks.ReentrantLock lock = new ReentrantLock(); public void testMethod() { try { if (lock.tryLock(1, TimeUnit.SECONDS)) { //获取到锁lock,同步块 } else { //没有获取到锁lock } } catch (InterruptedException e) { e.printStackTrace(); } finally { if (lock.isHeldByCurrentThread()) //若是当前线程持有锁lock,则释放锁lock lock.unlock(); } }
3、条件Condition的使用
条件Condition能够由锁lock来建立,实现多路通知的机制。
具备await、signal、signalAll的方法,与wait/notify相似,须要在获取锁后方能调用。
private final java.util.concurrent.locks.Lock lock = new ReentrantLock(); private final java.util.concurrent.locks.Condition condition = lock.newCondition(); public void await() { try { lock.lock(); //获取到锁lock condition.await();//等待condition通讯信号,释放condition锁 //接到condition通讯 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock();//释放对象锁lock } }
ReentrantReadWriteLock是对ReentrantLock 更进一步的扩展,实现了读锁readLock()(共享锁)和写锁writeLock()(独占锁),实现读写分离。读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提高了读写的性能。
读锁示例:
private final java.util.concurrent.locks.ReadWriteLock lock = new ReentrantReadWriteLock(); public void method() { try { lock.readLock().lock(); //获取到读锁readLock,同步块 } finally { lock.readLock().unlock();//释放读锁readLock } }
写锁示例:
private final java.util.concurrent.locks.ReadWriteLock lock = new ReentrantReadWriteLock(); public void method() { try { lock.writeLock().lock(); //获取到写锁writeLock,同步块 } finally { lock.writeLock().unlock(); //释放写锁writeLock } }
(1)同步容器
包括两部分:
一个是早期JDK的Vector、Hashtable;
一个是它们的同系容器,JDK1.2加入的同步包装类,使用Collections.synchronizedXxx工厂方法建立。
Map<String, Integer> hashmapSync = Collections.synchronizedMap(new HashMap<>());
同步容器都是线程安全的,一次只有一个线程访问容器的状态。
但在某些场景下可能须要加锁来保护复合操做。
复合类操做如:新增、删除、迭代、跳转以及条件运算。
这些复合操做在多线程并发的修改容器时,可能会表现出意外的行为,
最经典的即是ConcurrentModificationException,
缘由是当容器迭代的过程当中,被并发的修改了内容,这是因为早期迭代器设计的时候并无考虑并发修改的问题。
其底层的机制无非就是用传统的synchronized关键字对每一个公用的方法都进行同步,使得每次只能有一个线程访问容器的状态。这很明显不知足咱们今天互联网时代高并发的需求,在保证线程安全的同时,也必须有足够好的性能。
(2)并发容器
与Collections.synchronizedXxx()同步容器等相比,util.concurrent中引入的并发容器主要解决了两个问题:
1)根据具体场景进行设计,尽可能避免synchronized,提供并发性。
2)定义了一些并发安全的复合操做,而且保证并发环境下的迭代操做不会出错。
util.concurrent中容器在迭代时,能够不封装在synchronized中,能够保证不抛异常,可是未必每次看到的都是"最新的、当前的"数据。
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
ConcurrentHashMap 替代同步的Map即(Collections.synchronized(new HashMap()))。众所周知,HashMap是根据散列值分段存储的,同步Map在同步的时候会锁住整个Map,而ConcurrentHashMap在设计存储的时候引入了段落Segment定义,同步的时候只须要锁住根据散列值锁住了散列值所在的段落便可,大幅度提高了性能。ConcurrentHashMap也增长了对经常使用复合操做的支持,好比"若没有则添加":putIfAbsent(),替换:replace()。这2个操做都是原子操做。注意的是ConcurrentHashMap 弱化了size()和isEmpty()方法,并发状况尽可能少用,避免致使可能的加锁(固然也可能不加锁得到值,若是map数量没有变化的话)。
CopyOnWriteArrayList和CopyOnWriteArraySet分别代替List和Set,主要是在遍历操做为主的状况下来代替同步的List和同步的Set,这也就是上面所述的思路:迭代过程要保证不出错,除了加锁,另一种方法就是"克隆"容器对象。---缺点也明显,占有内存,且数据最终一致,但数据实时不必定一致,通常用于读多写少的并发场景。
ConcurrentSkipListMap能够在高效并发中替代SoredMap(例如用Collections.synchronzedMap包装的TreeMap)。
ConcurrentSkipListSet能够在高效并发中替代SoredSet(例如用Collections.synchronzedSet包装的TreeMap)。
ConcurrentLinkedQuerue是一个先进先出的队列。它是非阻塞队列。注意尽可能用isEmpty,而不是size();
CountDownLatch是一个同步辅助类。
一般运用场景:
(1)做为启动信号:将计数 1 初始化的 CountDownLatch 用做一个简单的开/关锁存器,或入口。
通俗描述:田径赛跑运动员等待(每位运动员为一个线程,都在await())的"发令枪",当发令枪countDown(),喊0的时候,全部运动员跳过await()起跑线并发跑起来了。
(2)做为结束信号:在经过调用 countDown() 的线程打开入口前,全部调用 await 的线程都一直在入口处等待。用 N 初始化的 CountDownLatch 可使一个线程在 N 个线程完成某项操做以前一直等待,或者使其在某项操做完成 N 次以前一直等待。
通俗描述:某裁判,在终点等待全部运动员都跑完,每一个运动员跑完就计数一次(countDown())当为0时,就能够往下继续统计第一人到最后一个撞线的时间。
import java.util.concurrent.CountDownLatch; public class CountDownLatchTest { public static void main(String[] args) throws InterruptedException { final Runnable task = () -> { try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " end"); }; long time = new CountDownLatchTest().timeTasks(10, task); System.out.println("耗时:" + time + "ms"); } private long timeTasks(int nThreads, final Runnable task) throws InterruptedException { // 一个启动信号,在 driver 为继续执行 worker 作好准备以前,它会阻止全部的 worker 继续执行。 final CountDownLatch startSignal = new CountDownLatch(1); // 一个完成信号,它容许 driver 在完成全部 worker 以前一直等待。 final CountDownLatch doneSignal = new CountDownLatch(nThreads); for (int i = 0; i < nThreads; i++) { Thread t = new Thread(() -> { try { startSignal.await(); //阻塞于此,一直到startSignal计数为0,再往下执行 try { task.run(); } finally { doneSignal.countDown(); //doneSignal 计数减一,直到最后一个线程结束 } } catch (InterruptedException ignored) { } }); t.start(); } long start = System.currentTimeMillis(); startSignal.countDown(); // doneSignal 计数减一,为0,全部task开始并发执行run doneSignal.await(); // 阻塞于此,一直到doneSignal计数为0,再往下执行 */ long end = System.currentTimeMillis(); return end - start; } }
CyclicBarrier是一个同步辅助类。
CyclicBarrier让一个线程达到屏障时被阻塞,直到最后一个线程达到屏障时,屏障才会开门,全部被屏障拦截的线程才会继续执行
CyclicBarrier(int parties, Runnable barrierAction)构造函数,用于在全部线程都到达屏障后优先执行barrierAction的run()方法
使用场景:
能够用于多线程计算之后,最后使用合并计算结果的场景;
通俗描述:某裁判,在终点(await()阻塞处)等待全部运动员都跑完,全部人都跑完就能够作吃炸鸡啤酒(barrierAction),可是只要一我的没跑完就都不能吃炸鸡啤酒,固然也没规定他们同时跑(固然也能够,一块儿使用CountDownLatch)。
CyclicBarrier与CountDownLatch的区别:
CountDownLatch强调的是一个线程等待多个线程完成某件事,只能用一次,没法重置;
CyclicBarrier强调的是多个线程互相等待完成,才去作某个事情,能够重置。
import java.util.concurrent.CyclicBarrier; public class WorkerThread implements Runnable { private final CyclicBarrier cyclicBarrier; public WorkerThread(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } public static void main(String[] args) { int THREAD_NUM = 5; final CyclicBarrier cyclicBarrier = new CyclicBarrier(THREAD_NUM, new Runnable() { // 当全部线程到达barrier时执行 @Override public void run() { System.out.println("--------------Inside Barrier--------------"); } }); for (int i = 0; i < THREAD_NUM; i++) { new Thread(new WorkerThread(cyclicBarrier)).start(); } } @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " pre-working"); //线程在这里等待,直到全部线程都到达barrier。 cyclicBarrier.await(); System.out.println(Thread.currentThread().getName() + " working"); } catch (Exception e) { e.printStackTrace(); } } }
更多api:
int await(long timeout, TimeUnit unit) 在全部参与者都已经在此屏障上调用 await 方法以前将一直等待,或者超出了指定的等待时间。
Semaphore信号量是一个计数信号量。
能够认为,Semaphore维护一个许可集。若有必要,在许可可用前会阻塞每个 acquire(),而后再获取该许可。每一个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。
通俗描述:某个车库只有N个车位,车主们要泊车,请向车库保安处阻塞 acquire()等待获取许可证,当得到许可证,车主们才能够去泊车。当某个车主离开车位的时候,交还许可证release() ,从而其余阻塞等待的车主有机会得到许可证。
另外:
Semaphore 默认是非公平策略,容许线程“抢占插队”获取许可证。公平策略则是线程依照请求的顺序获取许可证,近似FIFO的策略方式
线程池是一种多线程的处理方式,利用已有线程对象继续服务新的任务(按照必定的执行策略),而不是频繁地建立销毁线程对象,由此提供服务的吞吐能力,减小CPU的闲置时间。具体组成部分包括:
a、线程池管理器(ThreadPool)用于建立和管理线程池,包括建立线程池、销毁线程池,添加新任务。
b、工做线程(Worker)线程池中的线程,闲置的时候处于等待状态,能够循环回收利用。
c、任务接口(Task)每一个任务必须实现的接口类,为工做线程提供调用,主要规定了任务的入口、任务完成的收尾工做、任务的状态。
d、等待队列(Queue)存放等待处理的任务,提供缓冲机制。
Executors框架提供了一些便利的执行策略。
java.util.concurrent.ExecutorService service = java.util.concurrent.Executors.newFixedThreadPool(100);
ExecutorService的生命周期有3个状态:运行、关闭(shutting down)、中止。
提交任务submit(xxx)扩展了基本方法 Executor.execute(java.lang.Runnable)。
Future<?> submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
shutdown() 启动一次顺序关闭,执行之前提交的任务,但不接受新任务。
List
一个简单的示例:
public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i < 100; i++) { executorService.submit(() -> System.out.println("哈哈")); } // 若是再也不须要新任务,请适当关闭executorService并拒绝新任务 executorService.shutdown(); }
ThreadPoolExecutor为Executors的线程池内部实现类。
构造函数详解:
参数名 | 做用 |
---|---|
corePoolSize | 核心线程池大小 |
maximumPoolSize | 最大线程池大小 |
keepAliveTime | 线程池中超过corePoolSize数目的空闲线程最大存活时间;能够allowCoreThreadTimeOut(true)使得核心线程有效时间 |
TimeUnit | keepAliveTime时间单位 |
workQueue | 阻塞任务队列 |
threadFactory | 新建线程工厂 |
RejectedExecutionHandler | 当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理 |
1.当线程池小于corePoolSize时,新提交任务将建立一个新线程执行任务,即便此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会建立新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
一个简单的示例:
public static void main(String[] args) { java.util.concurrent.ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 10, // corePoolSize 核心线程数 100, // maximumPoolSize 最大线程数 30, // keepAliveTime 线程池中超过corePoolSize数目的空闲线程最大存活时间; TimeUnit.SECONDS,// TimeUnit keepAliveTime时间单位 new LinkedBlockingQueue<>(1000),//workQueue 阻塞任务队列 Executors.defaultThreadFactory(), // threadFactory 新建线程的工厂 // RejectedExecutionHandler当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理 new ThreadPoolExecutor.AbortPolicy() ); for (int i = 0; i < 100; i++) { threadPoolExecutor.submit(() -> System.out.println("哈哈")); } // 若是再也不须要新任务,请适当关闭threadPoolExecutor并拒绝新任务 threadPoolExecutor.shutdown(); }
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
V get() throws InterruptedException, ExecutionException 若有必要,等待计算完成,而后获取其结果。
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 若有必要,最多等待为使计算完成所给定的时间以后,获取其结果(若是结果可用)。
FutureTask则是一个RunnableFuture
简单示例一:
public static void main(String[] args) throws InterruptedException { FutureTask<Integer> future = new FutureTask<>(() -> { // 返回一个值或受检查的异常 // throw new Exception(); System.out.println("Future task is running..."); Thread.sleep((int) (10000 * Math.random())); return new Random().nextInt(100); }); new Thread(future).start(); //模拟其余业务逻辑 Thread.sleep(1000); //Integer result = future.get(0, TimeUnit.SECONDS); Integer result = null; try { result = future.get(); System.out.println("Future task finished..."); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("result========" + result); }
简单示例二,采用Executors:
public static void main(String[] args) throws InterruptedException { java.util.concurrent.ExecutorService threadPoolExecutor = java.util.concurrent.Executors.newCachedThreadPool(); Future<Integer> future = threadPoolExecutor.submit(() -> { // 返回一个值或受检查的异常 System.out.println("Future task is running..."); Thread.sleep((int) (10000 * Math.random())); //throw new Exception(); return new Random().nextInt(100); }); // 若是再也不须要新任务,请适当关闭threadPoolExecutor并拒绝新任务 threadPoolExecutor.shutdown(); // 模拟其余业务逻辑 Thread.sleep(1000); // Integer result = future.get(0, TimeUnit.SECONDS); Integer result = null; try { result = future.get(); System.out.println("Future task finished..."); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("result========" + result); }
简单示例三,采用Executors+CompletionService:
public static void main(String[] args) throws InterruptedException { java.util.concurrent.ExecutorService threadPoolExecutor = java.util.concurrent.Executors.newCachedThreadPool(); java.util.concurrent.CompletionService<Integer> completionService = new java.util.concurrent.ExecutorCompletionService<>(threadPoolExecutor); final int threadNum = 10; for (int i = 0; i < threadNum; i++) { completionService.submit(new MyCallable(i + 1)); } // 若是再也不须要新任务,请适当关闭threadPoolExecutor并拒绝新任务 threadPoolExecutor.shutdown(); // 模拟其余业务逻辑 Thread.sleep(2000); for (int i = 0; i < threadNum; i++) { try { System.out.println("result========" + completionService.take().get()); } catch (ExecutionException e) { e.printStackTrace(); } } } static class MyCallable implements Callable<Integer> { private final int i; public MyCallable(int i) { super(); this.i = i; } @Override public Integer call() throws Exception { // 返回一个值或受检查的异常 // throw new Exception(); return i; } }
** 注意的是提交到CompletionService中的Future是按照完成的顺序排列的,而不是按照添加的顺序排列的。
其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具备排他性,即当某个线程进入方法,执行其中的指令时,不会被其余线程打断,而别的线程就像自旋锁同样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另外一个线程进入,这只是一种逻辑上的理解。其实是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。其中的类能够分红4组
基本类:AtomicInteger、AtomicLong、AtomicBoolean;
引用类型:AtomicReference、AtomicStampedRerence、AtomicMarkableReference;--AtomicStampedReference 或者 AtomicMarkableReference 解决线程并发中,致使的ABA问题
数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray ---数组长度固定不可变,但保证数组上每一个元素的操做绝对安全的
属性原子修改器(Updater):AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
Updater使用限制:
限制1:操做的目标不能是static类型,前面说到unsafe的已经能够猜想到它提取的是非static类型的属性偏移量,若是是static类型在获取时若是没有使用对应的方法是会报错的,而这个Updater并无使用对应的方法。
限制2:操做的目标不能是final类型的,由于final根本无法修改。
限制3:必须是volatile类型的数据,也就是数据自己是读一致的。
限制4:属性必须对当前的Updater所在的区域是可见的,也就是private若是不是当前类确定是不可见的,protected若是不存在父子关系也是不可见的,default若是不是在同一个package下也是不可见的。
简单示例:
private static final AtomicIntegerFieldUpdater<A> ATOMIC_INTEGER_FIELD_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue"); static class A { volatile int intValue = 100; }
线程安全就是每次运行结果和单线程运行的结果是同样的,并且其余的变量的值也和预期的是同样的。
线程安全就是说多线程访问同一代码,不会产生不肯定的结果。
线程安全问题可能是由全局变量和静态变量引发的,当多个线程对共享数据只执行读操做,不执行写操做时,通常是线程安全的;当多个线程都执行写操做时,须要考虑线程同步来解决线程安全问题。
多个线程操做一个资源的状况下,致使资源数据先后不一致。这样就须要协调线程的调度,即线程同步。 解决多个线程使用共通资源的方法是:线程操做资源时独占资源,其余线程不能访问资源。使用锁能够保证在某一代码段上只有一条线程访问共用资源。
有两种方式实现线程同步:
有时候线程之间须要协做和通讯。
实现线程通讯的几种方式: