一 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() {@Overridepublic 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() {@Overridepublic 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;}@Overridepublic 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() {@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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时,读锁才可以进入读取的方法。
