湘潭做网站电话磐石网络,龙岩kk社区,外贸网站官网怎么做,二手电商怎么做Java增强之并发编程
1 多线程
1.1 进程及线程
程序启动的时候#xff0c;电脑会把这个程序加载到内存#xff0c;在内存中需要给当前的程序分配一段的独立运行的空间#xff0c;这个空间就专门负责这个程序的运行。每个应用程序运行都需要在内存中有自己独立的运行空间电脑会把这个程序加载到内存在内存中需要给当前的程序分配一段的独立运行的空间这个空间就专门负责这个程序的运行。每个应用程序运行都需要在内存中有自己独立的运行空间互不影响。进程就是应用程序在内存中的独立空间负责当前应用程序的运行负责调度当前程序中的所有运行细节。
线程位于进程中负责当前进程中的某个具备独立运行资质的空间负责程序中具体的某个独功能的运行。一个进程中至少有一个线程可以并发运行多个线程。多线程之间共享同一个堆空间每个线程都有自己独立的栈空间
1.2 多线程
在一个进程中同时开启多个线程同时去完成某些任务。
一个进程可以并发运行多个线程提高运行效率。
1.3 多线程运行的原理
CPU在线程中做时间片的切换。程序的运行时CPU负责的其实CPU在运行程序时某个时刻只能运行一个程序CPU在多个程序之间进行高速的切换因为切换的频率和速度太快了所以我们是看不出来的。从而得知多线程虽然可以提高程序的运行效率但是不能无限制的开线程。
1.4 实现线程的方式
1继承Thread类
2实现Runnable接口
3Callable和FutureTask创建线程
4通过线程池创建线程
代码演示见之前的文章https://blog.csdn.net/weixin_43786255/article/details/92065717
1.5 线程状态图解
详细讲解见https://blog.csdn.net/weixin_43786255/article/details/92065717
2 java同步关键字
在多线程编程中为了达到线程安全的目的往往通过加锁的方式来实现。
2.1 synchronized
是JVM级别的锁在编译过程中在指令级别加入一些标识来实现。无法中断正在阻塞队列或者等待队列的线程。synchronize是java语言的内置特性锁的释放是由jvm决定的人工无法干预 。
常与wait(),notify(),notifyAll()方法联用调用wait()方法线程让出CPU释放锁进入等待状态waitting进入等待队列。当其他线程调用notify()随机唤醒等待队列中的一个线程或notifyAll()唤醒等待队列中的全部线程时没会将队列中的线程对象放入第二个阻塞队列状态是blocked等待锁被释放后开始竞争锁。
synchronize提供了偏向锁轻量级锁重量级锁。
锁的释放时机有两种情况 ①获取锁的线程操作完成该线程会自动释放锁 ②)获取锁的线程出现异常jvm会自动释放。
synchronized存在的问题 ①如果获取锁标记的线程不主动释放锁则未获取标记的只能等待而且人工无法干预②当多个线程读写文件时读读操作互相不影响但是synchronized仍然无法同时执行。
2.2 Lock
Lock锁是Java代码级别的用户可以主动添加锁但是必须手动释放锁。因此一般来说使用 Lock 必须在 try{}catch{}块中进行并且将释放锁的操作放在 finally 块中进行以保证锁一定被被释放防止死锁的发生。提供了公平锁轮询锁定时锁可中断锁等还增加了多路通知机制Condition可以用一个锁来管理多个同步块。
与synchronized的区别 ①synchronized 是 Java 语言的关键字因此是内置特性。Lock是一个类通过这个类可以实现同步访问 ②synchronized 由系统自动释放lock必须手动释放否则可能产生死锁。
常见方法如下
void lock()获取锁(阻塞,如果其他线程获取到锁,需要等待)boolean tryLock()获取锁(非阻塞,如果其他线程获取到锁,则返回false)boolean tryLock(long time, TimeUnit unit)获取锁(在指定的时间范围内获取,超时返回false)void lockInterruptibly()可中断锁void unlock()释放锁
ReentrantLock 类是唯一实现了Lock 接口的类并且 ReentrantLock 提供了更多的方法。
区别对待读、写的操作用ReadWriteLock接口的实现类ReentrantReadWriteLock 里面提供了很多丰富的方法不过最主要的有两个方法readLock()和 writeLock()用来获取读锁和写锁。
ReadWriteLock将读写操作分离处理 ①一个线程获取读锁,另外的线程可以获取读锁。但是不能获取写锁(必须等待读锁释放)。 ②一个线程获取写锁,那么一定要等待该线程释放写锁,其他线程才能执行读写操作。
2.3 死锁
死锁两个或两个以上的线程在执行过程中因争夺资源而造成的一种互相等待的现象会让程序挂起无法完成任务。
产生死锁的条件 ①互斥条件一个资源每次只能被一个进程使用 ②请求与保持条件一个进程因请求资源而阻塞式对已获得的资源保持不放 ③不剥夺条件进程已获得的资源在未使用完成之前不能强制剥夺 ④循环等待条件若干进程之间形成一种头尾相接的循环等待资源关系。
如何避免死锁避免相互等待设置标记位
死锁处理破坏条件
2.4 Volatile特殊域变量
首先了解在多线程编程中要解决的问题主要有以下三方面 ①原子性作为一个整体运行 ②可见性多个线程修改的内容是可见的。CPU不是直接和系统内存通信而是把变量读到内部的缓冲也叫私 有的数据工作栈修改也是在内部缓存中但是何时修改到系统内存不能确定这个时间差就可能导致读到的值不是最新 值。 ③指令重排虚拟机把代码编译成指令后出于优化保证代码不变的情况下会调整指令的执行顺序
valotile能够满足可见性和有序性但无法保证原子性。 ①保证可见性在修改后强行把对变量的修改同步到系统内存中当其他CPu在读取自己内部缓存中的值发现是 volatile修饰的时候会把内部缓存中的值置为无效从系统内存中读取。 ②保证有序性在某些指令中插入屏障指令用于确保在向屏障指令后面继续执行的时候前面的所有指令已经 输入完毕。
原子性通过原子量Atomicxxx:原子量保证数据的原子性 2.5 ThreadLocal线程变量副本
ThreadLocal提供了线程本地变量访问本地变量的每个线程都会拷贝一个变量到自己的本地内存多个线程操作这个变量的时候实际上是操作自己本地内存里面的变量这样就不会对其他线程产生影响。
ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题ThreadLocal采用以时间换空间的方式同步机制采用以空间换时间的方式
作用保证线程的独立变量。
应用场景有变量或对象实例需要在线程中多个地方被重复使用不希望线程之间共享又不希望每次使用是都重新创建加大内存开销。
与Synchronized的区别Synchronized用于线程间的数据共享而ThreadLocal则用于线程间的数据隔离。
使用可以将可以将 ThreadLoadT视为 MapThread ,T把需要隔离的数据放入ThreadLocal通过threadLocal.set(val)赋值threadLocal.get()获取值最好不要放在线程池中避免复用。 3 并发包
3.1 java并发包
jdk1.5版本以后大多数的特性在 java.util.concurrent 包中是专门用于多线程发编程的充分利用了现代多处理器和多核心系统的功能以编写大规模并发应用程序。主要包含原子量、并发集合、同步器、可重入锁并对线程池的构造提供了强力 的支持。
3.2 线程池
通过重用现有的线程池而不是创建新的线程可以在处理多个请求的时候分摊在线程创建和销毁过程中产生的巨大开销当请求到达的时候工作线程已经存在不会由于等待创建线程而延迟执行从而提高系统的响应性。 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueRunnable workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); } corePoolSize: 核心池的大小(常驻线程数) maximumPoolSize: 最大的线程数(任务数) keepAliveTime: 线程空闲的等待时间 unit:等待时间的单位 workQueue: 并发的等待队列(如果任务数超过核心池的大小,任务需要在队列中等待,有界/无界) factory: 线程工厂,创建线程 handler: 任务拒绝的策略(队列中的任务数和核心池的任务数超过总的max/任务数超过核心池有界队列中的数目)
ThreadPoolExecutor的执行顺序 ①当线程数小于核心线程数时创建线程 ②当线程数大于核心线程数且队列未满时将任务放入任务队列 ③当线程数大于核心线程数时且队列任务已满若线程数小于最大线程数核心线程数有界队列的数目则创建线程若线程数大于最大线程数则抛出异常拒绝任务。
更多详细讲解见https://blog.csdn.net/weixin_43786255/article/details/92065717