一 Atmoic类synchronized加锁和look枷锁机制对比
控制线程同步的有Atmoic类和 synchronized加锁和look枷锁机制。但是一般情况下还是去优先选用 synchronized,主要原因有:
1.现在synchronized也被优化,性能也有提高
2.look加锁显然比synchronized控制的范围根据准确,性能高,但是代码却不易阅读。所以只有在及其需要性能时可以考虑使用
3.Atmoic对象只有在非常简单的情况下才有用,所以只有在性能方法的需求能够明确指示时才去考虑。
二、免锁容器:
容器也会应用于并非编程当中,**使得可以被并发的读取和写入**。早期的Vector和HashTable这类早期容器中具有许多的synchronized方法,当他们应用在非多线程中时,会导致不可接受的开销。所以javase5中特别添加了新的容器,通过使用更加灵活的技巧消除了锁,提高线程安全的性能。
1.免锁容器背后的策略:
对容器的修改与读取同时发生时,修改时会先复制容器数据,并在一个单独的副本上去执行修改操作,同时这个过程时不可见的,只有但修改完成时候,才会与源数据交换,而且这个交换的操作是原子性的。之后读取者就可以看到这个修改过的操作了。所以,容器在没用修改完成时去读取,这时读取者只能读取到没用修改的数据(源数据)
2.免锁容器:
CopyOnWriteArrayList:
允许多个迭代器同时去遍历修改,也允许列表被遍历时调用remove()不会去包ConcurrentModificationException异常
public class CopyArrayListDemo {
public static void main(String[] args) {
CopyOnWriteArrayList<String> copy = new CopyOnWriteArrayList<>();
copy.addAll(Arrays.asList("001", "002", "003"));
new Thread() {
@Override
public void run() {
System.out.println("被线程" + Thread.currentThread().getName() + "修改之前的集合" + copy);
for (int i = 0; i < copy.size(); i++) {
try {
copy.set(i, "0" + i);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
System.out.println("修改后的集合:");
for (String s : copy) {
System.out.println(s);
copy.remove(s);
}
System.out.println("remove:后的集合"+copy);
}
}.start();
}
}
其余CopyOnWriteArraySet和concurentHashMap、concurentLinkedQueue都使用了同样的技术。允许并非的读取和写入。同样在任何修改完成之前,读取者看不到他。只能看到源数据。
三、乐观锁:
尽管Atomic对象将执行像derementAndGet()这样的原子性操作,某些Atomic还允许你执行所谓的“乐观加锁”。即在执行某项计算时,实际上没用使用互斥,当准备更新这个对象时,需要使用一个compareAndSet()方法来将旧值和新值一起提交。如果旧值与它在对象中的旧址不同时,那么这个操作更新操作将失败。这个时候,我们必须在失败后去执行一些任务。
示列:乐观锁局基本原理
public class AtomicDemo {
public static void main(String[] args) {
AtomicInteger integer = new AtomicInteger();
integer.addAndGet(10);
new Thread(() -> {
int oldValue = integer.get();
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
int newValue = oldValue + 3;
System.out.println("老的值为" + oldValue + "要更新的值为 " + newValue);
if (!integer.compareAndSet(oldValue, newValue)) {
System.out.println(Thread.currentThread().getName() + "更新失败");
}
}).start();
new Thread(() -> {
int oldValue = integer.get();
int newValue = oldValue + 3;
if (!integer.compareAndSet(oldValue, newValue)) {
System.out.println("更新失败");
} else {
System.out.println(Thread.currentThread().getName() + "更新成功");
}
}).start();
}
}
上述程序便是乐观锁的原理:线程一要去跟新一个数据,但是遇到了延迟,这个时候线程二抢先跟新了。此时当延迟过后,线程一integer.compareAndSet(oldValue, newValue)的时候没发现,老的值和Aomtic对象中的值不同了,此时便跟新失败。这个时候我们是乐观的,我们希望数据没用任何任务插入修改。但又保持了数据的未锁状态。
四、ReadWriterLock:
ReadWriterLock对于向数据结构相对不频繁的写入,但是有多个任务要去经常的去读取这个数据时。如果写锁被其他任务持有,那么任何读锁不能访问,直到这个写锁被释放为止。当锁写被读锁获得时候,那么写锁同样等到读锁被释放之后才能进行。
class ReadDmo implements Runnable{
ReentrantReadWriteLock reent;
List list;
ReadDmo(List list,ReentrantReadWriteLock reent){
this.list=list;
this.reent=reent;
}
@Override
public void run() {
Lock lock = reent.readLock();
lock.lock();
try {
for (int i = 0; i < list.size(); i++) {
System.out.println(Thread.currentThread().getName()+":"+list.get(i));
}
}finally {
lock.unlock();
}
}
}
public class Demo {
public static void main(String[] args) {
ReentrantReadWriteLock reent = new ReentrantReadWriteLock();
final List<Integer> list = new ArrayList<>();
new Thread() {
@Override
public void run() {
Lock lock = reent.writeLock();
lock.lock();
try {
for (int i = 0; i < 20; i++) {
list.add(i);
}
} finally {
lock.unlock();//如果不释放锁,读取的任务将被阻塞
}
}
}.start();
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i <4 ; i++) {
service.execute(new ReadDmo(list,reent));
}
}
}
**同样当锁先被读取锁获得时,如果不释放那么写锁也将进入等待知道读取锁释放**
public class ReadDemo {
static volatile boolean falg = false;
private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
Lock lock = ReadDemo.lock.readLock();
try {
System.out.println("进入读锁");
lock.lock();
TimeUnit.SECONDS.sleep(100);
System.out.println("写锁释放");
lock.unlock();
} catch (Exception e) {
System.out.println("");
}
}).start();
TimeUnit.MILLISECONDS.sleep(10);
new Thread(() -> {
try {
Lock lock = ReadDemo.lock.writeLock();
System.out.println("进入读取线程,但会被阻塞");
try {
lock.lock();
System.out.println("进入写锁");
lock.unlock();
} catch (Exception e) {
e.printStackTrace();
}
} finally {
}
}).start();
}
}
示列: ReadWriterLock配合Boolean类型控制先写入在读取
public class ReaderWriterFile {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
volatile boolean falg=false;
private class WriterTask implements Runnable{
private String fileName;
private String content;
WriterTask(String fileName,String content){
this.fileName = fileName;
this.content=content;
}
@Override
public void run() {
System.out.println("11111");
Lock wlock = lock.writeLock();
wlock.lock();
try {
BufferedWriter writer = new BufferedWriter(
new FileWriter(new File(fileName), true));
writer.write(content);
TimeUnit.SECONDS.sleep(1);
writer.flush();
writer.close();
falg=true;
}catch (Exception e){
} finally {
wlock.unlock();
}
}
}
private class ReaderTask implements Runnable{
private String fileName;
ReaderTask(String fileName){
this.fileName = fileName;
}
@Override
public void run() {
System.out.println("=====");
while (!Thread.interrupted()) {
Lock rlock = lock.readLock();
rlock.lock();
try {
BufferedReader reader = new BufferedReader(new FileReader(new File(fileName)));
String temp;
while ((temp = reader.readLine()) != null) {
System.out.println(temp);
}
} catch (Exception e) {
} finally {
rlock.unlock();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
final ReaderWriterFile readerWriterFile = new ReaderWriterFile();
WriterTask writerTask = readerWriterFile.new WriterTask("a", "higklmn" + " 111111" + "22");
final ReaderTask readerTask = readerWriterFile.new ReaderTask("a");
new Thread(readerTask).start();
new Thread(readerTask).start();
new Thread(readerTask).start();
new Thread(writerTask).start();
}
}
程序中的两个读取线程同时进行,但是值有当写入数据的程执行完毕之后,将flag变为true时,读锁才可以进入读取的方法。