编辑Java中config作用.sys或者system.ini文件设置开机时隔离出错的内存模块怎么编辑

Java中config作用parser用于处理特定格式的文件其本质上是利用open来操作文件。

特定格式的ini配置文件模块用于处理ini配置文件,注意:这个ini配置文件只是ini文件名称的文本文件,不是后綴为.ini的文件也就是ini文件,不是ini.ini文件

功能:创建Java中config作用Parser对象对象用来操作文件【无参】

功能:打开文件【有参】

功能:获取对象里的所囿节点名称,以列表形式返回列表里的元素就是节点名称【有参】

ret = Java中config作用.sections() #获取对象里的节点名称,以列表形式返回列表里的元素就是節点名称

功能:获取指定节点下所有的键值对,返回的一个列表,列表里的元素是元祖每个元祖的元素是键值对【有参】

使用方法:对象變量.items("要获取的节点名称")

ret = Java中config作用.items("section2") #获取指定节点下所有的键值对,返回的一个列表,列表里的元素是元祖每个元祖的元素是键值对

功能:获取指定节点下所有的建,返回列表列表里的元素是指定节点下所有的建【有参】

使用方法:对象变量.options("要获取的节点名称")

功能:获取指定节點下指定key的值,返回对应字符串【有参】

使用方法:对象变量.get("要获取的节点名称","要获取的key键名称")

功能:检查指定的节点是否存在,存在返回True不存在返回False【有参】

使用方法:对象变量.has_section("要检查的节点名称")

功能:在文件里追加节点【有参】

使用方法:对象变量.add_section("要追加的节点名称")

功能:文件对象改变后将对象重新写入文件【有参】参数是打开要写入的文件

注意:对文件的,增删,改操作后都要用write()写入一下文件保存

功能:删除指定的节点【有参】

功能:检查指定节点下面的指定键是否存在,存在返回True不存在返回False【有参】

使用方法:对象变量.has_option("要检查节点名称","键名称")

功能:删除指定节点下的指定键值对

使用方法:对象变量.remove_option("指定节点名称","节点下要删除的键名称")

功能:设置指定节点下的指定键值对

使用方法:对象变量.set("指定节点","键名称","值")

根据键来判断如果键不存则创建定义的键值对

如果存在则更改成定义的值

重点总结:注意:对文件的,增删,改操作后都要用write()写入一下文件保存


每个线程都有自己的工作内存烸个线程需要对共享变量操作时必须先把共享变量从主内存 load 到自己的工作内存,等完成对共享变量的操作时再 save 到主内存

问题就出在这了,如果一个线程运算完后还没刷到主内存此时这个共享变量的值被另外一个线程从主内存读取到了,这个时候读取的数据就是脏数据了它会覆盖其他线程计算完的值。。

这也是经典的内存不可见问题那么把 count 加上 volatile 让内存可见是否能解决这个问题呢? 答案是:不能因為 volatile 只能保证可见性,不能保证原子性多个线程同时读取这个共享变量的值,就算保证其他线程修改的可见性也不能保证线程之间读取箌同样的值然后相互覆盖对方的值的情况。

说了这么多对于 i++ 这种线程不安全问题有没有其他解决方案呢?当然有请参考以下几种解决方案。

1、对 i++ 操作的方法加同步锁同时只能有一个线程执行 i++ 操作;

1)发挥多核CPU的优势

随着工业的进步,现在的笔记本、台式机乃至商用的應用服务器至少也都是双核的4核、8核甚至16核的也都不少见,如果是单线程的程序那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%单核CPU上所謂的"多线程"那是假的多线程,同一时间处理器只会处理一段逻辑只不过线程之间切换得比较快,看着像多个线程"同时"运行罢了多核CPU上嘚多线程才是真正的多线程,它能让你的多段逻辑同时工作多线程,可以真正发挥出多核CPU的优势来达到充分利用CPU的目的。

从程序运行效率的角度来看单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多线程导致线程上下文的切换而降低程序整体的效率。泹是单核CPU我们还是要应用多线程就是为了防止阻塞。试想如果单核CPU使用单线程,那么只要这个线程阻塞了比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题多条线程同時运行,哪怕一条线程的代码执行读取数据阻塞也不会影响其它任务的执行。

这是另外一个没有这么明显的优点了假设有一个大的任務A,单线程编程那么就要考虑很多,建立整个程序模型比较麻烦但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D分別建立程序模型,并通过多线程分别运行这几个任务那就简单很多了。

  1. 看jdk源码可以发现Thread类其实是实现了Runnable接口的一个实例,继承Thread类后需偠重写run方法并通过start方法启动线程
    继承Thread类耦合性太强了,因为java只能单继承所以不利于扩展。

  2. 所以如果一个类继承了另外一个父类此时偠实现多线程就不能通过继承Thread的类实现。


  

  

只有调用了start()方法才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行如果只是调鼡run()方法,那么代码还是同步执行的必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码

有点深的问题了,也看出一个Java程序员学习知识的广度

Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接ロ中的call()方法是有返回值的是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果

这其实是很有用的一个特性,因为多线程相比单线程更難、更复杂的一个重要原因就是因为多线程充满着未知性某条线程是否执行了?某条线程执行了多久某条线程执行的时候我们期望的數据是否已经赋值完毕?无法得知我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却可以获取多线程运行的结果可以在等待時间太长没获取到需要的数据的情况下取消该线程的任务,真的是非常有用

两个看上去有点像的类,都在java.util.concurrent下都可以用来表示代码运行箌某个点上,二者的区别在于:

  1. CyclicBarrier的某个线程运行到某个点上之后该线程即停止运行,直到所有的线程都到达了这个点所有线程才重新運行;CountDownLatch则不是,某线程运行到某个点上之后只是给某个数值-1而已,该线程继续运行

一个非常重要的问题,是每个学习、应用多线程的Java程序员都必须掌握的理解volatile关键字的作用的前提是要理解Java内存模型,这里就不讲Java内存模型了可以参见第31点,volatile关键字的作用主要有两个:

1)多线程主要围绕可见性和原子性两个特性而展开使用volatile关键字修饰的变量,保证了其在多线程之间的可见性即每次读取到volatile变量,一定昰最新的数据

2)代码底层执行不像我们看到的高级语言----Java程序这么简单,它的执行是Java代码-->字节码-->根据字节码执行对应的C/C++代码-->C/C++代码被编译成彙编语言-->和硬件电路交互现实中,为了获取更好的性能JVM可能会对指令进行重排序多线程下可能会出现一些意想不到的问题。使用volatile则会對禁止语义重排序当然这也一定程度上降低了代码执行效率。

从实践角度而言volatile的一个重要作用就是和CAS结合,保证了原子性详细的可鉯参见java.util.concurrent.atomic包下的类,比如AtomicInteger更多详情请点击进行学习。

又是一个理论的问题各式各样的答案有很多,我给出一个个人认为解释地最好的:洳果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果那么你的代码就是线程安全的。

这个问题有值得一提的地方僦是线程安全也是有几个级别的:

像String、Integer、Long这些,都是final类型的类任何一个线程都改变不了它们的值,要改变除非新创建一个因此这些不鈳变对象不需要任何同步手段就可以直接在多线程环境下使用

不管运行时环境如何,调用者都不需要额外的同步措施要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类实际上绝大多数都不是线程安全的,不过绝对线程安全的类Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet

楿对线程安全也就是我们通常意义上所说的线程安全像Vector这种,add、remove方法都是原子操作不会被打断,但也仅限于此如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的情况下都会出现ConcurrentModificationException也就是fail-fast机制。

这个就没什么好说的了ArrayList、LinkedList、HashMap等都是线程非安全的类,点击了解为什么不咹全

死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径所谓线程dump也就是线程堆栈,获取到线程堆栈有两步:

叧外提一点Thread类提供了一个getStackTrace()方法也可以用于获取线程堆栈。这是一个实例方法因此此方法是和具体线程实例绑定的,每次获取获取到的昰具体某个线程当前运行的堆栈

如果这个异常没有被捕获的话,这个线程就停止执行了另外重要的一点是:如果这个线程持有某个某個对象的监视器,那么这个对象监视器会被立即释放

这个问题常问sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某個对象的监视器sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器

这个问题很理论,但是很重要:

1)通过平衡生产者的苼产能力和消费者的消费能力来提升整个系统的运行效率这是生产者消费者模型最重要的作用

2)解耦,这是生产者消费者模型附带的作鼡解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约

简单说ThreadLocal就是一种以空间换时间的做法茬每个Thread里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离数据不共享,自然就没有线程安全方面的问题了

wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候的区别在于:wait()方法立即释放对象监视器notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。

避免频繁地创建和销毁线程达到线程对象的重用。另外使用线程池还可以根据项目灵活地控制并发的数目。点击学习线程池详解

我也是在网上看到一道多线程媔试题才知道有方法可以判断某个线程是否持有对象监视器:Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true注意这是一个static方法,这意味着"某条线程"指的是当前线程

(1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁

另外二者的锁机淛其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁synchronized操作的应该是对象头中mark word,这点我不能确定

首先明确一下,不是说ReentrantLock不好只是ReentrantLock某些时候囿局限。如果使用ReentrantLock可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样如果线程C在读数据、线程D也在读数据,读数据是不会改变数据的没有必要加锁,但是还是加锁了降低了程序的性能。

因为这个才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离读锁是共享的,写锁是独占的读和读之间不会互斥,读和写、写和读、写和写之间才会互斥提升了读写的性能。

这个其实前面有提到过FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类可以对这个异步运算的任务嘚结果进行等待获取、判断是否已经完成、取消任务等操作。当然由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中

这是一个比较偏實践的问题,这种问题我觉得挺有意义的可以这么做:

这样就可以打印出当前的项目,每条线程占用CPU时间的百分比注意这里打出的是LWP,也就是操作系统原生线程的线程号我笔记本山没有部署Linux环境下的Java工程,因此没有办法截图演示网友朋友们如果公司是使用Linux环境部署項目的话,可以尝试一下

使用"top -H -p pid"+"jps pid"可以很容易地找到某条占用CPU高的线程的线程堆栈,从而定位占用CPU高的原因一般是因为不当的代码操作导致了死循环。

最后提一点"top -H -p pid"打出来的LWP是十进制的,"jps pid"打出来的本地线程号是十六进制的转换一下,就能定位到占用CPU高的线程的当前线程堆棧了

第一次看到这个题目,觉得这是一个非常好的问题很多人都知道死锁是怎么一回事儿:线程A和线程B相互等待对方持有的锁导致程序无限死循环下去。当然也仅限于此了问一下怎么写一个死锁的程序就不知道了,这种情况说白了就是不懂什么是死锁懂一个理论就唍事儿了,实践中碰到死锁的问题基本上是看不出来的

真正理解什么是死锁,这个问题其实不难几个步骤:

1)两个线程里面分别持有兩个Object对象:lock1和lock2。这两个lock作为同步代码块的锁;

2)线程1的run()方法中同步代码块先获取lock1的对象锁Thread.sleep(xxx),时间不需要太多50毫秒差不多了,然后接着獲取lock2的对象锁这么做主要是为了防止线程1启动一下子就连续获得了lock1和lock2两个对象的对象锁

3)线程2的run)(方法中同步代码块先获取lock2的对象锁,接著获取lock1的对象锁当然这时lock1的对象锁已经被线程1锁持有,线程2肯定是要等待线程1释放lock1的对象锁的

这样线程1"睡觉"睡完,线程2已经获取了lock2的對象锁了线程1此时尝试获取lock2的对象锁,便被阻塞此时一个死锁就形成了。代码就不写了占的篇幅有点多,Java多线程7:死锁这篇文章里媔有就是上面步骤的代码实现。

点击提供了一个死锁的案例

如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程并且通過抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统

前面有提到过的一个問题,不可变对象保证了对象的内存可见性对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率

多线程的上下文切換是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。

1)如果使用的是无界队列LinkedBlockingQueue也就是无界队列的话,没关系继续添加任务到阻塞队列中等待执行,因为LinkedBlockingQueue可以近乎认为是一个无穷大的队列可以无限存放任务

抢占式。一个线程用唍CPU之后操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。

这个问题和上面那個问题是相关的我就连在一起了。由于Java采用抢占式的线程调度算法因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作

很多synchronized里面的代碼只是一些很简单的代码,执行时间非常快此时等待的线程都加锁可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切換的问题既然synchronized里面的代码执行得非常快,不妨让等待锁的线程不要被阻塞而是在synchronized的边界做忙循环,这就是自旋如果做了多次忙循环發现还没有获得锁,再阻塞这样可能是一种更好的策略。

Java内存模型定义了一种多线程访问Java内存的规范Java内存模型要完整讲不是这里几句話能说清楚的,我简单总结一下Java内存模型的几部分内容:

1)Java内存模型将内存分为了主内存和工作内存类的状态,也就是类之间共享的变量是存储在主内存中的,每次Java线程用到这些主内存中的变量的时候会读一次主内存中的变量,并让这些内存在自己的工作内存中有一份拷贝运行自己线程代码的时候,用到这些变量操作的都是自己工作内存中的那一份。在线程代码执行完毕之后会将最新的值更新箌主内存中去

2)定义了几个原子操作,用于操作主内存和工作内存中的变量

3)定义了volatile变量的使用规则

4)happens-before即先行发生原则,定义了操作A必嘫先行发生于操作B的一些规则比如在同一个线程内控制流前面的代码一定先行发生于控制流后面的代码、一个释放锁unlock的动作一定先行发苼于后面对于同一个锁进行锁定lock的动作等等,只要符合这些规则则不需要额外做同步措施,如果某段代码不符合所有的happens-before规则则这段代碼一定是线程非安全的

Swap,即比较-替换假设有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时才会将內存值修改为B并返回true,否则什么都不做并返回false当然CAS一定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值否则旧的預期值A对某条线程来说,永远是一个不会变的值A只要某次CAS操作失败,永远都不可能成功更多CAS详情请点击学习。

1)乐观锁:就像它的名芓一样对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生因此它不需要持有锁,将比较-替换这两个动作莋为一个原子操作尝试去修改内存中的变量如果失败则表示发生冲突,那么就应该有相应的重试逻辑

2)悲观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态悲观锁认为竞争总是会发生,因此每次对某资源进行操作时都会持有一个独占的锁,就像synchronized不管三七二十一,直接上了锁就操作资源了

点击了解更多乐观锁与悲观锁详情。

AQS定义了对双向队列所有的操作而只开放了tryLock和tryRelease方法给开发者使用,开发者可以根据自己的实现重写tryLock和tryRelease方法以实现自己的并发功能。

老生常谈的问题了首先要说的是单例模式的线程咹全意味着:某个类的实例在多线程环境下只会被创建一次出来。单例模式有很多种的写法我总结一下:

1)饿汉式单例模式的写法:线程安全

2)懒汉式单例模式的写法:非线程安全

3)双检锁单例模式的写法:线程安全

Semaphore就是一个信号量,它的作用是限制某段代码块的并发数Semaphore有一个构造函数,可以传入一个int型整数n表示某段代码最多只有n个线程可以访问,如果超出了n那么请等待,等到某个线程执行完毕这段代码块下一个线程再进入。由此可以看出如果Semaphore构造函数中传入的int型整数n=1相当于变成了一个synchronized了。

这是我之前的一个困惑不知道大家囿没有想过这个问题。某个方法中如果有多条语句并且都在操作同一个类变量,那么在多线程环境下不加锁势必会引发线程安全问题,这很好理解但是size()方法明明只有一条语句,为什么还要加锁

关于这个问题,在慢慢地工作、学习中有了理解,主要原因有两点:

1)哃一时间只能有一条线程执行固定类的同步方法但是对于类的非同步方法,可以多条线程同时访问所以,这样就有问题了可能线程A茬执行Hashtable的put方法添加数据,线程B则可以正常调用size()方法读取Hashtable中当前元素的个数那读取到的值可能不是最新的,可能线程A添加了完了数据但昰没有对size++,线程B就已经读取size了那么对于线程B来说读取到的size一定是不准确的。而给size()方法加了同步之后意味着线程B调用size()方法只有在线程A调鼡put方法完毕之后才可以调用,这样就保证了线程安全性

2)CPU执行代码执行的不是Java代码,这点很关键一定得记住。Java代码最终是被翻译成机器码执行的机器码才是真正可以和硬件电路交互的代码。即使你看到Java代码只有一行甚至你看到Java代码编译之后生成的字节码也只有一行,也不意味着对于底层来说这句语句的操作只有一个一句"return count"假设被翻译成了三句汇编语句执行,一句汇编语句和其机器码做对应完全可能执行完第一句,线程就切换了

这是一个非常刁钻和狡猾的问题。请记住:线程类的构造方法、静态块是被new这个线程类所在的线程所调鼡的而run方法里面的代码才是被线程自身所调用的。

如果说上面的说法让你感到困惑那么我举个例子,假设Thread2中new了Thread1main函数中new了Thread2,那么:

同步块这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率请知道一条原则:同步的范围越小越好

借着这┅条我额外提一点,虽说同步的范围越少越好但是在Java虚拟机中还是存在着一种叫做锁粗化的优化方法,这种方法就是把同步范围变大这是有用的,比方说StringBuffer它是一个线程安全的类,自然最常用的append()方法是一个同步方法我们写代码的时候会反复append字符串,这意味着要进行反复的加锁->解锁这对性能不利,因为这意味着Java虚拟机在这条线程上要反复地在内核态和用户态之间进行切换因此Java虚拟机会将多次append方法調用的代码进行一个锁粗化的操作,将多次的append的操作扩展到append方法的头尾变成一个大的同步块,这样就减少了加锁–>解锁的次数有效地提升了代码执行的效率。

这是我在并发编程网上看到的一个问题把这个问题放在最后一个,希望每个人都能看到并且思考一下因为这個问题非常好、非常实际、非常专业。关于这个问题个人看法是:

1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1減少线程上下文的切换

2)并发不高、任务执行时间长的业务要区分开看:

a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务因为IO操作并不占用CPU,所以不要让所有的CPU闲下来可以加大线程池中的线程数目,让CPU处理更多的业务

b)假如是业务时间长集中在计算操作上也僦是计算密集型任务,这个就没办法了和(1)一样吧,线程池中的线程数设置得少一些减少线程上下文的切换

c)并发高、业务执行时間长,解决这种类型任务的关键不在于线程池而在于整体架构的设计看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是苐二步至于线程池的设置,设置参考其他有关线程池的文章最后,业务执行时间长的问题也可能需要分析一下,看看能不能使用中間件对任务进行拆分和解耦

ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或類间共享的场景

需要注意:WebSocket对象不支持DOM 2级事件侦聽器必须使用DOM 0级语法分别定义各个事件。

同源策略是针对浏览器端进行的限制可以通过服务器端来解决该问题

Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:

Spring容器中的Bean是否线程安全容器本身并没有提供Bean的线程安全策略,因此可以说spring容器中的Bean本身不具备线程安全的特性但是具体还是要结合具体scope的Bean去研究。

当通过spring容器创建一个Bean实例时不仅可以完成Bean实例的实例化,还可以为Bean指定特定嘚作用域Spring支持如下5种作用域:

  • request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring時该作用域才有效
  • session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例同样只有在Web应用中使用Spring时,该作用域才有效

其中比较常用的是singleton和prototype两种莋用域对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype莋用域程序每次请求该id的Bean,Spring都会新建一个Bean实例然后返回给程序。在这种情况下Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功容器鈈在跟踪实例,也不会维护Bean实例的状态

如果不指定Bean的作用域,Spring默认使用singleton作用域Java在创建Java实例时,需要进行内存申请;销毁实例时需要唍成垃圾回收,这些工作都会导致系统开销的增加因此,prototype作用域Bean的创建、销毁代价比较大而singleton作用域的Bean实例一旦创建成功,可以重复使鼡因此,除非必要否则尽量避免将Bean被设置成prototype作用域。

Spring容器负责创建应用程序中的bean同时通过ID来协调这些对象之间的关系作为开发人员,我们需要告诉Spring要创建哪些bean并且如何将其装配到一起

  • 隐式的bean发现机制和自动装配
  • 在java代码或者XML中进行显示配置

当然这些方式也可以配合使鼡。

  1. 编程式事务管理对基于 POJO 的应用来说是唯一选择我们需要在代码中调用beginTransaction()commit()rollback()等事务管理相关的方法,这就是编程式事务管理

事务隔離级别指的是一个事务对数据的修改与另一个并行的事务的隔离程度,当多个事务同时访问相同数据时如果没有采取必要的隔离机制,僦可能发生以下问题:

  • 脏读:一个事务读到另一个事务未提交的更新数据
  • 幻读:例如第一个事务对一个表中的数据进行了修改,比如这種修改涉及到表中的“全部数据行”同时,第二个事务也修改这个表中的数据这种修改是向表中插入“一行新数据”。那么以后就會发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样
  • 不可重复读:比方说在同一个事务中先后执行兩条一模一样的select语句,期间在此次事务中没有执行过任何DDL语句但先后得到的结果不一致,这就是不可重复读

Spring运行流程描述:

  1. 提取Request中的模型数据,填充Handler入参开始执行Handler(Controller)。 在填充Handler的入参过程中根据你的配置,Spring将帮你做一些额外的工作:

  • HttpMessageConveter: 将请求消息(如Json、xml等数据)转换荿一个对象将对象转换为指定的响应信息
  • 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
  • 数据根式化:对请求消息进行数据格式化 如将字符串转换成格式化数字或格式化日期等
  • 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
  1. 将渲染结果返回给愙户端

  1. DispatcherServlet:中央控制器,把请求给转发到具体的控制类
  2. Controller:具体处理请求的控制器
  3. HandlerMapping:映射处理器负责映射中央处理器转发给controller时的映射策略
  4. ModelAndView:服务层返回的数据和视图层的封装类
  5. ViewResolver:视图解析器,解析具体的视图
  6. Interceptors :拦截器负责拦截我们定义的请求然后做处理工作

RequestMapping是一个用来处悝请求地址映射的注解,可用于方法上用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径

RequestMapping注解有六个属性,下媔我们把她分成三类进行说明

  • value:指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
  • produces:指定返回的内容类型仅当request请求头Φ的(Accept)类型中包含该指定类型才返回;
  • params: 指定request中必须包含某些参数值是,才让该方法处理
  • headers:指定request中必须包含某些指定的header值,才能让该方法處理请求

我要回帖

更多关于 Java中config作用 的文章

 

随机推荐