乐观锁和悲观锁是什么
悲观锁
认为自己在使用数据的时候一定会有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。synchronized和ReentrantLock实现类都是悲观锁。
适合写操作多的场景,现价所可以保证写操作时的数据正确。 狼性锁
乐观锁
认为自己在使用数据是不会有别的线程来修改数据或者资源,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制Version和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁CAS自旋实现的。如果数据没有被更新,当前线程将自己修改的数据成功写入,如果这个数据已经被其他的线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改,重试抢锁。 佛系锁
《阿里巴巴java开发手册》
【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能
锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
8锁案例以及原理解释
第一种:原理我们在某个方法上加了synchronized ,锁的就不是这个方法,是整个资源类。
一个对象里面如果有多个synchronized方法,某一时刻,只要有一个线程去调用其中的synchronized方法了,其他的对象只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法,锁的是当前this,被锁定后,其他的线程都不能进入到当前对象的其他的synchronized方法
class Phone{
public synchronized void sendEmail(){
System.out.println("email"); }
public synchronized void sendSMS(){
System.out.println("sms");
} }
//线程操作资源类
public class LockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendEmail();
},"a").start();
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(()->{
phone.sendSMS();
},"b").start();
}
}
输出 email sms
第二种:原理同上
class Phone{
public synchronized void sendEmail(){
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("email");
}
public synchronized void sendSMS(){
System.out.println("sms");
}
public void hello(){
System.out.println("hello");
}
}
//线程操作资源类
public class LockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendEmail();
},"a").start();
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(()->{
phone.sendSMS();
},"b").start();
}
}
输出 email sms
第三种:
加个普通方法后发现和同步锁无关换成两个对象后,不是同一把锁了,情况立刻变化。
class Phone{
public synchronized void sendEmail(){
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("email");
}
public synchronized void sendSMS(){
System.out.println("sms");
}
public void hello(){
System.out.println("hello");
}
}
//线程操作资源类
public class LockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendEmail();
},"a").start();
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(()->{
phone.hello();
},"b").start();
}
} 输出 hello email
第四种:原理同上
class Phone{
public synchronized void sendEmail(){
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("email");
}
public synchronized void sendSMS(){
System.out.println("sms");
}
public void hello(){
System.out.println("hello");
}
}
//线程操作资源类
public class LockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone.sendEmail();
},"a").start();
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(()->{
phone2.sendSMS();
},"b").start();
}
}输出 sms email
第五种方法:
都换成静态同步方法后,情况又变化
三种 synchronized 锁的内容有一些差别:
对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁——>实例对象本身,
对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板
对于同步方法块,锁的是 synchronized 括号内的对象
class Phone{
public static synchronized void sendEmail(){
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("email");
}
public static synchronized void sendSMS(){
System.out.println("sms");
}
public void hello(){
System.out.println("hello");
}
}
//线程操作资源类
public class LockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone.sendEmail();
},"a").start();
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(()->{
phone.sendSMS();
},"b").start();
}
} 输出 email sms
第六种:类锁
class Phone{
public static synchronized void sendEmail(){
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("email");
}
public static synchronized void sendSMS(){
System.out.println("sms");
}
public void hello(){
System.out.println("hello");
}
}
//线程操作资源类
public class LockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone.sendEmail();
},"a").start();
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(()->{
phone2.sendSMS();
},"b").start();
}
} 输出 email sms
第七种:
当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。
所有的普通同步方法用的都是同一把锁——实例对象本身,就是new出来的具体实例对象本身,本类this
也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。
所有的静态同步方法用的也是同一把锁——类对象本身,就是我们说过的唯一模板Class
具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。
class Phone{
public static synchronized void sendEmail(){
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("email");
}
public synchronized void sendSMS(){
System.out.println("sms");
}
public void hello(){
System.out.println("hello");
}
}
//线程操作资源类
public class LockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone.sendEmail();
},"a").start();
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(()->{
phone.sendSMS();
},"b").start();
}
}输出sms email
第八种
class Phone{
public static synchronized void sendEmail(){
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("email");
}
public synchronized void sendSMS(){
System.out.println("sms");
}
public void hello(){
System.out.println("hello");
}
}
//线程操作资源类
public class LockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(()->{
phone.sendEmail();
},"a").start();
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
new Thread(()->{
phone2.sendSMS();
},"b").start();
}
}输出sms email