乐观锁和悲观锁
悲观锁:
顾名思义:很悲观,感觉程序一定会出问题,所以在程序中加入锁。主要有:
1、synchronized(有对象锁,类锁),
对象锁,在synchronized关键字后的传入线程的共享对象。或者是在实力方法的中用synchronized关键字修饰。
类锁,就是在静态方法中使用sychroniezd的关键字修饰。
2、lock锁(实现类Reentrantlock)
主要可以完成sychronid完成的一切工作,但是可以自由的控制什么时候释放锁,有更好的性能。
乐观锁:
顾名思义:很乐观啊,感觉程序很健壮,在什么时候都不会出问题。所以都不加锁。主要使用:
1、数据库版本号机制:
在数据库中一般都是使用innnodb数据库引擎,是行级锁,myisam是表级锁。安全但是效率低。
在表中多添加一个字段version,当需要对某条数据进行修改操作的时候,就连同这个version字段一同取出。
比如说对password字段进行修改,那么会将这个取出来的version值进行加一,然后再去原始的数据库中查看原始的version值。
如果加一后的version值比数据库中的version值大,那么就进行修这条数据行。否则就修改失败。
2、cas指令。
比如说在内存中有一个共享变量i=0,有两个线程想要操作这块数据。
如果说,A线程抢到cpu执行权。读取主内存中的共享变量i=0, 那么就把这个共享变量i=0的副本保存到线程A的本地内存中。
然后开始对 i +1操作。
而这个时候,A的cpu时间片执行完了,回到就绪状态,
而线程B抢到了cpu执行权,开始执行对主内存中共享变量i的操作
线程B也把i=0保存到自己的本地内存中,然后进行i+1操作,得到 i=1;然后再去主内存中读取共享变量i=0。
线程B发现两次读取主内存中共享变量的值一样,那么就主内存中的共享变量的i的值修改为1。
公平锁和非公平锁
主要是在lock()方法上传一个true/false就可以实现公平锁,和非公平锁。
公平锁:
公平锁就是在线程同步的基础上,一个个线程排着队。
当一个线程拿到了对象锁之后执行程序,释放对象锁之后,就能再去使用使用cpu的空闲时间去执行。
非公平锁:
就是在释放掉对象锁的时候,其他线程还没来得及执行的时候,又去执行了程序。
比如说:排队挂号。你排完队走了,轮到下一个人挂号了,但是又有事想问挂号人员。
这个时候你在下一个人还没开始办理业务的,你又问了一句,这个科室在几楼。这就是非公平锁。
=========================================================================
死锁和活锁
死锁:原因是竞争太激烈了。谁也不想放掉手里的资源。还都想得到对方的资源。
一个很好的成语更好的描述死锁。鹬蚌相争。
比如说连个线程A B 同时共享 两个共享对象o1 o2,
A B两个线程只有拿到两把锁才能完成任务。
而A线程拿到O1对象锁,然后在锁池中找o2对象锁。然后就一直找啊找。
而B线程已经在所池中找到了o2对象锁,线程B在锁池中一直找O1对象锁。
这个时候,程序没有异常,这回永无止境的执行。成为死锁。
活锁:原因都是太谦让了。在一个三叉路口ABC三条路,其中有AB两条路各有一辆车,但都想去第三条路C,
A然B先走,B让A先走。一直的谦让下去。程序没有执行。并且永无止境的执行。称为活锁。
产生死锁的4个条件:
资源互斥条件:一把对象锁只能被一个线程占用。
请求与保持条件:我拿着这一把锁,再去找另一把锁。
不剥夺条件:一个线程拿到了资源,但没使用完,就一直不放手。
循环等待条件:没找到对象锁就一直等;
怎么避免死锁:
1、尽量不适用Synchronized进行嵌套使用。
2、都每个锁都是用时间的限制。如果某个线程持有的对象的时间到了,就立即释放锁。
3、对获得锁的顺序进行限制。按顺序获得锁,只要拿到A对象锁,才能拿到b对象锁。这样就直避免死锁。
自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,
这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
可重入锁
reentrantlock
对于同一个线程在外层方法获取锁的时候,在进入内层方法时也会自动获取锁。
优点可以在一定程度上避免死锁。
独享锁和共享锁
两种锁只是一种概念
独享锁:该锁一次只能被一个线程所持有
共享锁:该锁可以被多个线程所持有
举例:
synchronized是独享锁;
可重入锁ReentrantLock是独享锁;
读写锁ReentrantReadWriteLock中的读锁ReadLock是共享锁,写锁WriteLock是独享锁。
独享锁与共享锁通过AQS(AbstractQueuedSynchronizer)来实现的,通过实现不同的方法,来实现独享或者共享。
互斥锁和读写锁
互斥锁的具体实现就是synchronized、ReentrantLock。ReentrantLock是JDK1.5的新特性,
采用ReentrantLock可以完全替代替换synchronized传统的锁机制,更加灵活。
读写锁的具体实现就是读写锁ReadWriteLock。
锁池
假如线程A拿到了对象锁开始执行程序,那么线程B、线程C、线程D 也想这行被Synchronized修饰的代码、但是对象锁
被线程A拿着,但是A线程还在占着对象锁,那么这个时候,线程BCD就会在一个地方等待A线程释放这把对象锁。
这个等待的地方就是锁池。
等待区:
我们知道生产者在消费者中。在处于某个对象上活动的线程。当这个对象调用了wait方法的时候,
处于在这个的对象上的线程,会立即释放占有的对象锁。并且该线程会不会直接进入锁池,会进入
等待区,等待该线程被唤醒,当这个对象调用notify和notifyAll方法的时候。该线程会进入锁池
继续寻找对象锁。