视频:https://www.bilibili.com/video/av79732701
====================================
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
CAS(Compare and Swap 比较并交换),当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
CAS操作中包含三个操作数——需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B,否则处理器不做任何操作。
===================================
private MyThreadOne o = new MyThreadOne();
public void m(){
synchronized (o){
}
}
对 堆内存 里的对象进行加锁,对象的头部信息 锁标记+1
每次new一个对象太麻烦,锁定对象可指向当前类
public void m1(){
synchronized (this){
}
}
第一行就加锁,锁住所有代码,因此上面等同于
public synchronized void m1(){
synchronized (this){
}
}
public synchronized static void m1(){
}
//锁定静态方法 相当于如下
public static void m2(){
synchronized (MyThreadOne.class){
}
}
问题: 有两个方法,线程一执行方法一,此方法加锁,锁定当前对象;线程二执行方法二,方法二不加锁,请问能同时执行么?
public class MyThreadOne {
public static void main(String[] args) {
MyThreadOne myThreadOne = new MyThreadOne();
new Thread(myThreadOne::m1).start();
new Thread(myThreadOne::m).start();
}
public void m(){ }
public synchronized void m1(){}
}
答:能。m1执行虽然对当前对象加锁。但m执行并不需要锁。
若一个属性的getter和setter方法,两个方法都需要加锁,否则会出现脏读
synchronized 的锁是可重入的
1 一个方法A,加锁对象o, A中调用另一个方法B,B方法也加锁对象o,可以调用,锁标记从1变成2
即:一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁
2 一个子类方法,加锁对象o,子类方法调用父类方法,父类方法虽然也是方法上加锁 synchronized,但锁住的是子类对象,可以调用
程序在执行过程中,如果出现异常,默认情况锁会被释放。
所以在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况
volatile:使一个变量在多个线程中可见
volatile并不能保证多个线程共同修改变量时所带来的不一致问题,也就是说volatile不能代替synchronizad
问:比较volatile和synchronized:
volatile: 可见性,但不能保证一致性,效率高
synchronizad:可见性,一致性,效率低
AtomXXX 原子类,比 synchronizad效率高很多,用非常底层的语言编写的
AtomicInteger num = new AtomicInteger();
num.set(1);//设置 1
num.incrementAndGet();// num++,返回2 num.decrementAndGet()同理
num.getAndIncrement();// num++,返回1 num.getAndDecrement()同理
num.get();//获得当前值
num.getAndSet(10);// 设置10,但返回老的值
System.out.println();
单个操作是原子性,但若是加上 if( num.get() <100)然后执行++操作也是会出现数据不同步的
锁定某个对象,若锁定对象的属性发生改变,不影响锁
但是如果栈中的变量 重新 new 一个对象,或者被赋值,改变了 指针指向的对象,则下一个对象锁定的就是新的对象。避免如此使用。
String ss = "heool";
不要以字符串常量作为锁定对象,String sss = "heool";用ss和sss锁会是同一把锁。
题目:实现一个容器,提供两个方法add,size。写两个线程,线程一添加10个元素到容器中,线程二实现监控元素的个数,当个数到5个时,给出提示并结束
第一版:
public class MyThreadOne {
public static void main(String[] args){
MyThreadOne myThreadOne = new MyThreadOne();
new Thread(()->{
for (int i =0;i< 10;i++){
myThreadOne.add(new MyThreadOne());
System.out.println("线程一添加了第 "+(i+1)+" 个元素");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
while (true){
if(myThreadOne.size() == 5){
System.out.println("线程二检测到第五个数据,结束");
break;
}
}
}).start();
}
volatile List<MyThreadOne> list = new LinkedList<>();
public void add(MyThreadOne myThreadOne){
list.add(myThreadOne);
}
public int size(){return list.size();}
}
但线程2死循环, 浪费cpu
第二版:
public class MyThreadTwo {
public static void main(String[] args) throws InterruptedException {
MyThreadTwo myThreadOne = new MyThreadTwo();
final Object lock = new Object();
new Thread(()->{
synchronized (lock) { // 第一次 获得lock锁
try {
lock.wait(); // 第一次 暂停,并且释放lock锁
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程二检测到第五个数据,结束");
}
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
for (int i = 0; i < 10; i++) {
myThreadOne.add(new MyThreadTwo());
System.out.println("线程一添加了第 " + (i + 1) + " 个元素");
if (myThreadOne.size() == 5) {
synchronized (lock) { // 第二次 线程2 获得lock锁
lock.notify(); // 第二次 线程2 唤醒其他线程,但并没有释放锁
}//第二次 线程2 释放锁
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
volatile List<MyThreadTwo> list = new LinkedList<>();
public void add(MyThreadTwo myThreadOne){
list.add(myThreadOne);
}
public int size(){return list.size();}
}
第三版,用CountDownLatch /CyclicBarrier /Semaphore
public class MyThreadThree {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
MyThreadThree myThreadOne = new MyThreadThree();
new Thread(()->{
try {
if (countDownLatch.getCount() > 0)
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程二检测到第五个数据,结束");
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
for (int i = 0; i < 10; i++) {
myThreadOne.add(new MyThreadThree());
System.out.println("线程一添加了第 " + (i + 1) + " 个元素");
if (myThreadOne.size() == 5) {
countDownLatch.countDown();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
volatile List<MyThreadThree> list = new LinkedList<>();
public void add(MyThreadThree myThreadOne){
list.add(myThreadOne);
}
public int size(){return list.size();}
}