一、锁诞生的背景:
咱们说的锁,不是每家每户的门锁,咱们说的锁是多线程并发场景下,或者分布式集群下,对共享变量的修改,需要增加锁的机制,保证数据操作的安全性,影响数据的安全性包含以下几个方面:
1)原子性:
常见的面试问题 i++是否线程安全??
针对 i++,cpu需要分三步操作:
A、读取i
B 、累加运算
C、写回缓存
这样看i++并不是一个原子操作,所以是不安全的。
2)可见性:
可见性问题,也是比较常见,A线程对共享变量的修改,对B线程不可知,导致数据一致性问题。
3)有序性:
有序性,是指CPU指令执行层面,指令执行的流程可能根咱们的程序不太一致。
日常除了上面说的多线程并发层面的锁,还有经常遇到以下类型的锁:
1)日常在Mysql数据库中,innodb存储引擎,采用行锁,防止多个更新请求对同一条记录的修改操作;
2)在集群模式下,单台应用的软硬件优化已经达到极致,为了支撑更多的并发,采用水平扩展,大家应用集群;在集群模式下,多个节点对共享数据的访问,就会有新的概念“分布式锁”,可以用redis,zk,MySQL行锁等等方式实现。
这是目前大家经常遇到的锁的概念,今天我们重点介绍单节点,多线程并发场景下,数据安全问题;
二、初识synchronized:
以jdk1.6版本作为分水岭,在之前没有偏向锁,轻量级锁的概念,synchronized是重量级锁,我们先从用法上来认识synchronized
package com.jason.juc;
public class TestMain {
public int counter=0;
public void increCounter()
{
counter=counter+1;
}
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
public static void main(String[] args) throws InterruptedException {
final TestMain testMain=new TestMain();
Thread[] threads=new Thread[2];
for(int i=0;i<threads.length;i++)
{
threads[i]=new Thread("thread"+i){
@Override
public void run() {
for(int j=0;j<10000;j++)
{
testMain.increCounter();
}
}
};
threads[i].start();
}
threads[0].join();
threads[1].join();
System.out.println("-----main=="+testMain.getCounter());
}
}
理想情况下,运行结果是20000,但是实际情况并非如此:
接下来,我们来看一下synchronized的日常用法:
大致用法可以分为两块:
1)修饰方法
2)修饰代码块
// public synchronized void increCounter()//修饰非静态方法,表示锁的是当前对象
// {
// counter=counter+1;
// }
// public static synchronized