首先需要注意的是同步访问会让并行变为串行,但安全和效率,这两个之间没有折衷,安全第一,但我们要追求的必须是高效安全
因为并发的访问共享资源导致状态不一致造成的安全问题,就叫安全性失败,我们通过同步化访问来解决。
什么时候会出现共享受限资源的冲突?
有一份数据摆在这里,多个worker线程都对其进行修改,状态就可能会乱了
总之,每次访问一个资源时,从进去到出来,都要保证数据的一致性
基本上所有保护共享受限资源的方法,都是序列化对受限资源的访问(同步化),也就是程序到这里就变成串行了,加个锁保证同时只有一个线程访问,这种机制就叫互斥量
如果你改变一个对象的状态是一个复杂的过程:
* 这期间你最好保证不要出现任何被打断的可能
原子操作被用来写无锁的代码,避免同步
原子操作不是同步化的,而是避免了同步化:
——普通的运算操作,如果要依赖原子性,要谨慎使用,至少编程思想里不推荐的,除非非常懂JVM,能编写JVM,编程思想就是这个意思
——但是可以使用Atom系列类来保证安全
有两部分内容:
——普通的运算操作的原子性,如加减乘除,这个很难搞懂,你知道a+b是不是原子操作?
——Atom系列类,提供了一套原子操作,基本还是有保障的
普通运算的原子性:暂时不做研究了
a++不是原子操作
+-*/也不是原子操作
x = x + 1 =也不是原子操作
想了解更多,再看一遍编程思想
原子类是可以信赖的,可以用来做性能调优,避免写同步代码,避免序列化访问资源
public class EvenGenerator extends IntGenerator {
private int currentEvenValue = 0;
public int next() {
++currentEvenValue; // Danger point here!
++currentEvenValue;
return currentEvenValue;
}
public static void main(String[] args) {
EvenChecker.test(new EvenGenerator());
}
}
原子操作就是一步能完成的操作:
AtomicInteger currentEvenValue = new AtomicInteger(0);
return currentEvenValue.addAndGet(2); //这里给value增加了2,并返回其值
注意:
都有什么
例子:
public class SynchronizedEventGenerator extends IntGenerator {
private int currentEvenValue = 0;
public int next() {
synchronized (this) {
++currentEvenValue;
++currentEvenValue;
return currentEvenValue;
}
}
public static void main(String[] args) {
EvenChecker.test(new SynchronizedEventGenerator());
}
}
public synchronized int next() {
++currentEvenValue; // Danger point here!
++currentEvenValue;
return currentEvenValue;
}
相当于:
public int next() {
synchronized (this) {
++currentEvenValue;
++currentEvenValue;
return currentEvenValue;
}
}
synchronized的锁始终是加在一个对象上
synchronized的原理:
注意:
wait,notify和synchronized的用法
import java.util.concurrent.locks.*;
public class MutexEvenGenerator extends IntGenerator {
private int currentEvenValue = 0;
private Lock lock = new ReentrantLock();
public int next() {
lock.lock();
try {
++currentEvenValue;
Thread.yield(); // Cause failure faster
++currentEvenValue;
return currentEvenValue;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
EvenChecker.test(new MutexEvenGenerator());
}
}
比synchronized多了什么特性:
——可以尝试获取锁,不必非得阻塞在这
——提供了比synchronized更细粒度的控制
——在实现链表遍历节点时,有个节点传递的加锁机制(锁耦合),在释放这个节点的锁之前,必须捕获下个节点的锁
——synchronized引起的阻塞无法被interrupt方法中断,但ReentrantLock提供了可以被中断的机制
——ReentrantLock.lockInterruptly():如果得不到锁(被其他地方占用),就会阻塞,但是这个阻塞可以被interrupt()
例子:
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
public class AttemptLocking {
private ReentrantLock lock = new ReentrantLock();
public void untimed() {
boolean captured = lock.tryLock();
try {
System.out.println("tryLock(): " + captured);
} finally {
if (captured)
lock.unlock();
}
}
public void timed() {
boolean captured = false;
try {
captured = lock.tryLock(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
System.out.println("tryLock(2, TimeUnit.SECONDS): " + captured);
} finally {
if (captured)
lock.unlock();
}
}
public static void main(String[] args) {
final AttemptLocking al = new AttemptLocking();
al.untimed(); // True -- lock is available
al.timed(); // True -- lock is available
// Now create a separate task to grab the lock:
new Thread() {
{
setDaemon(true);
}
public void run() {
al.lock.lock();
System.out.println("acquired");
}
}.start();
Thread.yield(); // Give the 2nd task a chance
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
al.untimed(); // False -- lock grabbed by task
al.timed(); // False -- lock grabbed by task
}
} /*
* Output: tryLock(): true tryLock(2, TimeUnit.SECONDS): true acquired
* tryLock(): false tryLock(2, TimeUnit.SECONDS): false
*/// :~
boolean captured = lock.tryLock();//不会阻塞,不管有没有得到锁,都往下执行
captured = lock.tryLock(2, TimeUnit.SECONDS); //会阻塞2秒,然后不管有没有得到锁,都往下执行
其他高级锁,不好意思,我还没研究过
这个,也不好意思,我也没研究过