java多线程的同步
使用线程锁synchronized内置锁 线程开始运行,拥有自己的栈空间,就如同一个脚本一样,按照既定的代码一步一步地执行,直到终止。但是,每个运行中的线程,如果仅仅是孤立地运行,那么没有一点儿价值,或者说价值很少,如果多个线程能够相互配合完成工作,包括数据之间的共享,协同处理事情。这将会带来巨大的价值。
Java支持多个线程同时访问一个对象或者对象的成员变量,关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,又称为内置锁机制。
synchronized 的性质可重入性
同一线程的外层方法获得锁后,内层方法可以直接再次获取该锁;
避免死锁,提升封装性;
关键字:同一线程,同一把锁
不可中断性
一旦这个锁已经被别人获得了,如果我还想获得,我只能选择等待或者阻塞,直到别的线程释放这个锁;如果别的线程永远不释放锁,那么我只能永远等待;
相比之下,Lock类提供的锁,拥有中断能力;第一,如果我觉得等待的时间太长了,有权中断现在已经获取到锁的线程的执行;第二,如 ...
认识Java里的线程
认识Java里的线程Java程序天生就是多线程的 一个Java程序从main()方法开始执行,然后按照既定的代码逻辑执行,看似没有其他线程参与,但实际上Java程序天生就是多线程程序,因为执行main()方法的是一个名称为main的线程。
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
public class MultiThread {
public static void main(String[] args) {
//获取Jav线程管理MXBean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
//不需要获取同步的monitor和synchronizer信息,仅获取线程和线程堆栈信息
ThreadInfo[] thr ...
多线程概述
多线程概述
基础概念进程和线程
进程是程序运行资源分配的最小单位
进程是操作系统进行资源分配的最小单位,其中资源包括:CPU、内存空间、磁盘IO等,同一进程中的多条线程共享该进程中的全部系统资源,而进程和进程之间是相互独立的。进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。显然,程序是死的、静态的,进程是活的、动态的。进程可以分为系统进程和用户进程。凡是用于完成操作系统的各种功能的进程就是系统进程,它们就是处于运行状态下的操作系统本身,用户进程就是所有由你启动的进程。
线程是CPU调度的最小单位,必须依赖于进程而存在
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的、能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
线程无处不在
任何一个程序都必须要创建线程,特别是Java不管任何程序都必 ...
java中的读写锁
ReentrantReadWriteLock 读写锁 之前提到锁(如Mutex和ReentrantLock)基本都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。
除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁能够简化读写交互场景的编程方式。假设在程序中定义一个共享的用作缓存数据结构,它大部分时间提供读服务(例如查询和搜索),而写操作占有的时间很少,但是写操作完成之后的更新需要对后续的读服务可见。
在没有读写锁支持的(Java 5之前)时候,如果需要完成上述工作就要使用Java的等待通知机制,就是当写操作开始时,所有晚于写操作的读操作均会进入等待状态,只有写操作完成并进行通知之后,所有等待的读操作才能继续执行(写操作之间依靠synchronized关键进行同步),这样做的目的是使读操作能读取到正确的数据,不会出现脏读。改用读写锁实现上述功能,只需要在读操作时获取读锁,写操作时获 ...
java中的显示锁Lock
java中的显示锁Lock
什么是显示锁在Java 1.5之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile两种。Java1.5增加了一种新的机制,Lock,Lock是一个接口,提供了无条件的、可轮询的、定时的、可中断的锁获取操作,所有的加锁和解锁操作方法都是显示的,因而称为显示锁,Lock并不是替代内置加锁的方法,而是当内置加锁机制不适用时,作为一种可选择的高级功能。
Lock和synchronized的比较
synchronized代码更简洁
Lock可以在获取锁可以被中断,超时获取锁,尝试获取锁
synchronized在1.8以前是性能低下的,到了1.8之后经过改良,性能基本行和Lock相持平,如果不是特殊场景推荐使用synchronized。
Lock 的实现类
ReentrantLock
ReentrantReadWriteLock
Lock的APIpublic interface Lock {
//获取锁
void lock();
//可中断锁,在获取锁的过程中可以中断线程
void lockInte ...
java中的活锁
什么是活锁 两个线程在尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生同一个线程总是拿到同一把锁,在尝试拿另一把锁时因为拿不到,而将本来已经持有的锁释放的过程。
百度定义:活锁指的是任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。 活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
造成活锁的原因 当一系列封锁不能按照其先后顺序执行时,就可能导致一些事务无限期等待某个封锁,从而导致活锁。
活锁的解决 每个线程休眠随机数,错开拿锁的时间。
活锁重现
还拿我们死锁中转账的业务,也可以使用显示锁来解决
/**
* 活锁
* 转账业务
*/
public class TransferMoneyDeadlock {
public static void transfer(Account from, Account to, int amount) {
//自旋 一直尝试到转账成功
while (true) {
...
java中的死锁
JAVA中的死锁
什么是死锁 在多线程环境中,多个进程可以竞争有限数量的资源。当一个进程申请资源时,如果这时没有可用资源,那么这个进程进入等待状态。有时,如果所申请的资源被其他等待进程占有,那么该等待进程有可能再也无法改变状态。这种情况称为死锁
在Java中使用多线程,就会有可能导致死锁问题。死锁会让程序一直卡住,不再程序往下执行。我们只能通过中止并重启的方式来让程序重新执行。
造成死锁的原因
当前线程拥有其他线程需要的资源
当前线程等待其他线程已拥有的资源
都不放弃自己拥有的资源
死锁的必要条件互斥 进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
不可剥夺 进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
请求与保持 进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
循环等待 是指进程发生死锁后,必然存在一个进程–资源之间的环形链,通俗讲就是你等我 ...
MyBatis-statement模块
MYBATIS-statement模块
概述 StatementHandler负责处理Mybatis与JDBC之间Statement的交互,而JDBC中的Statement,我们在学习JDBC的时候就了解过,就是负责与数据库进行交互的对象。这其中会涉及到一些对象,我们用到的时候再学习。首先,我们来看下StatementHandler的体系结构。
StatementHandler接口
statementHandler接口的实现大致有四个,其中三个实现类都是和JDBC中的Statement响对应的:
SimpleStatementHandler,这个很简单了,就是对应我们JDBC中常用的Statement接口,用于简单SQL的处理;
PreparedStatementHandler,这个对应JDBC中的PreparedStatement,预编译SQL的接口;
CallableStatementHandler,这个对应JDBC中CallableStatement,用于执行存储过程相关的接口;
RoutingStatementHandler,这个接口是以上三个接口的路由,没有实际操作,只是负 ...
MyBatis-执行器模块
执行器模块
Executor是MyBaits核心接口之一,定义了数据库操作最基本的方法,SqlSession的功能都是基于它来实现的
什么是执行器 什么是执行器?Mybatis中所有的Mapper语句的执行都是通过Executor进行的,Executor是Mybatis的一个核心接口,其定义如下。从其定义的接口方法我们可以看出,对应的增删改语句是通过Executor接口的update方法进行的,查询是通过query方法进行的。虽然Executor接口的实现类有BaseExecutor和CachingExecutor,而BaseExecutor的子类又有SimpleExecutor、ReuseExecutor和BatchExecutor,但BaseExecutor是一个抽象类,其只实现了一些公共的封装,而把真正的核心实现都通过方法抽象出来给子类实现,如doUpdate()、doQuery();CachingExecutor只是在Executor的基础上加入了缓存的功能,底层还是通过Executor调用的,所以真正有作用的Executor只有SimpleExecutor、ReuseEx ...
MyBatis-代理封装
binding模块
这个模块有四个类,这四个类是层层调用的关系,对外的是MapperRegistry,映射器注册器。它会被Configuration类直接调用,用于将用户自定义的映射器全部注册到注册器中,而这个注册器显而易见会保存在Configuration实例中备用。
其实看到这个名称,我们就会想起之前解析的类型别名注册器与类型处理器注册器,其实他们之间的目的差不多,就是注册的内容不同罢了,映射器注册器注册的是MyBatis使用者自定义的各种映射器。
MapperRegistry类
MapperRegistry是Mapper接口及其对应的代理对象工厂的注册中心
/**
* 映射器注册器
* 是Mapper接口及其对应的代理对象工厂的注册中心
*
* @author Clinton Begin
* @author Eduardo Macarron
* @author Lasse Voss
*/
public class MapperRegistry {
//mybatis全局唯一的配置对象,包含所有信息
private final Configuration ...
MyBatis-缓存模块
mybatis缓存 MyBatis作为一个强大的持久层框架,缓存是其必不可少的功能之一,MyBatis中的缓存是两层结构的,分为一级缓存,二级缓存,但本质上市相同的,它们使用的都是Cache接口的实现。
MyBatis缓存的实现是基于Map的,从缓存里面读写数据是缓存模块的核心基础功能
除核心功能之外,有很多额外的附加功能,如:防止缓存击穿,添加缓存情况策略(fifo、LRU),序列化功能,日志能力和定时清空能力等
附加功能可以以任意的组合附加到核心基础功能之上
装饰者模式
mybatis 缓存模块使用了装饰器模式
Mybatis缓存的核心模块就是在缓存中读写数据,但是除了在缓存中读写数据wait,还有其他的附加功能,这些附加功能可以任意的加在缓存这个核心功能上。
加载附加功能可以有很多种方法,动态代理或者继承都可以实现,但是附加功能存在多种组合,用这两种方法,会导致生成大量的子类,所以mybatis选择使用装饰器模式.(灵活性、扩展性)。
类结构Mybatis 数据源的实现主要是在 cache包:
Cache 接口
一级缓存和二级缓存都是通过cache接口来实现的
C ...
MongoDB建模
MongoDB 建模MongoDB 的数据模式是一种灵活模式,关系型数据库要求你在插入数据之前必须先定义好一个表的模式结构,而 MongoDB 的集合则并不限制 document 结构。这种灵活性让对象和数据库文档之间的映射变得很容易。即使数据记录之间有很大的变化,每个文档也可以很好的映射到各条不同的记录。 当然在实际使用中,同一个集合中的文档往往都有一个比较类似的结构。
数据模型设计中最具挑战性的是在应用程序需求,数据库引擎性能要求和数据读写模式之间做权衡考量。当设计数据模型的时候,一定要考虑应用程序对数据的使用模式(如查询,更新和处理)以及数据本身的天然结构。
MongoDB 数据建模入门
参考:https://docs.mongodb.com/guides/server/introduction/#what-you-ll-need
(一)定义数据集当需要建立数据存储时,首先应该思考以下问题:需要存储哪些数据?这些字段之间如何关联?
这是一个数据建模的过程。目标是将业务需求抽象为逻辑模型。
假设这样一个场景:我们需要建立数据库以跟踪物料及其数量,大小,标签和等级。
如果是存储在 ...