标签:代码块 ons 之间 关键字 同步锁 asn override task 系统
目录:
什么是内存可见性:当多个线程操作共享数据时,彼此不可见。
demo:测试线程数据没有及时与主内存数据进行同步
package com.oy; public class TestVolatile { public static void main(String[] args) throws InterruptedException { MyRunnable myRunnable = new MyRunnable(); new Thread(myRunnable).start(); while (true) { // 下面这个判断 myRunnable.isFlag() 一直是 false // 主线程 Thread.sleep 后,才有时间同步线程数据 // Thread.sleep(1000); if (myRunnable.isFlag()) { System.out.println("========"); break; } } } } class MyRunnable implements Runnable { private boolean flag = false; @Override public void run() { try { Thread.sleep(200); } catch (Exception e) { } flag = true; System.out.println("flag=" + isFlag()); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
现象:主线程没有添加 Thread.sleep(1000); 这句代码时,主线程不会打印 "========";添加后会打印。
原因:while(true) 效率很高,主线程没有及时将数据与主内存进行同步。共享变量保存在主内存中,子线程操作该变量时,复制一份进行操作,然后同步到主内存中。但是这个过程其他线程不可见。
解决方案一:使用 synchronized 会取同步数据
synchronized (myRunnable) { if (myRunnable.isFlag()) { System.out.println("========"); break; } }
解决方案二:使用 volatile 关键字修饰变量
private volatile boolean flag = false;
volatile 关键字:当多个线程操作共享数据时,可以保证内存中的数据可见。相较于 synchronized 是一种较为轻量级的同步策略。
注意:
i++ 的原子性问题:i++ 操作时实际上分为三个步骤“读-改-写”
int i = 10; i = i++; // 上面 i++ 操作系统底层的过程分为三步 int temp = i; i = i + 1; i = temp;
package com.oy; public class TestAtomicDemo { public static void main(String[] args) { AtomicDemo ad = new AtomicDemo(); for (int i = 0; i < 10; i++) { new Thread(ad).start(); } } } class AtomicDemo implements Runnable { private int serialNumber = 0; @Override public void run() { try { Thread.sleep(200); } catch (Exception e) { } System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber()); } public int getSerialNumber() { return serialNumber++; } }
现象:
原因:因为变量 serialNumber 的自增 (serialNumber++) 操作不是原子性操作,当多个线程操作 serialNumber 变量时就会有线程安全问题。
解决方案:
原子变量:jdk1.5 后 java.util.concurrent.atomic 包下提供了常用的原子变量。
CAS 算法时硬件对于并发操作共享数据的支持。CAS 保护三个操作数:内存值 V,预估值(旧值)A,更新值 B,当且仅当 V==A 时, V = B, 否则不做任何操作。(就是只有预估值与内存值相等时,才进行更新)
当某个线程判断预估值与内存值不等时,不进行任何处理,但是会发起重试,直至成功。
package com.oy; import java.util.concurrent.atomic.AtomicInteger; public class TestAtomicDemo { public static void main(String[] args) { AtomicDemo ad = new AtomicDemo(); for (int i = 0; i < 10; i++) { new Thread(ad).start(); } } } class AtomicDemo implements Runnable { //private int serialNumber = 0; private AtomicInteger serialNumber = new AtomicInteger(); @Override public void run() { try { Thread.sleep(200); } catch (Exception e) { } System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber()); } public int getSerialNumber() { //return serialNumber++; return serialNumber.getAndIncrement(); } }
Java 5 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器的性能。
ConcurrentHashMap 同步容器类时 Java 5 增加的一个线程安全的哈希表。对于多线程的操作,介于 HashMap 与 Hashtable 之间。内部采用 “锁分段” 机制替代 Hashtable 的独占锁,进而提高性能。Hashtable 是在其内部的每个方法上添加 synchronized 关键字进行同步,性能低,且在复合操作(比如不存在则添加、存在则删除)时,由于调用了 Hashtable 的多个方法,同样有线程安全问题。
当使用 List<Stirng> list = Collections.synchronizedList<new ArrayList<String>()) 包装一个 ArrayList 来得到线程安全的 list 集合时同样有 “并发修改异常” 问题。
package com.oy; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class TestCopyOnWriteArrayList { public static void main(String[] args) { MyThread mt = new MyThread(); for (int i = 0; i < 5; i++) { new Thread(mt).start(); } } } class MyThread implements Runnable { //private static List<String> list = Collections.synchronizedList(new ArrayList<String>()); private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>(); static { list.add("aaa"); list.add("bbb"); list.add("ccc"); } @Override public void run() { Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); // 使用 Collections.synchronizedList 时,同样有并发修改异常 list.add("ddd"); } } }
CountDownLatch 内部维护一个 count, 如果count 不为 0,latch.await() 处于等待,直至 count 为 0。
下面案例:统计 5 个线程执行时间。每个子线程执行完后进行 latch.countDown(), 直至 count 为 0,程序从latch.await() 等待的地方继续执行。
package com.oy; import java.util.concurrent.CountDownLatch; public class TestCountDownLatch { public static void main(String[] args) { // 创建闭锁对象 int threadNum = 5; CountDownLatch latch = new CountDownLatch(threadNum); LatchDemo ld = new LatchDemo(latch); // 计算下面多个线程的执行时间 long start = System.currentTimeMillis(); for (int i = 0; i < threadNum; i++) { new Thread(ld).start(); } // latch.await(): 等待锁 countDown try { latch.await(); } catch (Exception e) { } long end = System.currentTimeMillis(); System.out.println("执行时间:" + (end - start) + " ms"); } } class LatchDemo implements Runnable { private CountDownLatch latch; public LatchDemo(CountDownLatch latch) { this.latch = latch; } @Override public void run() { synchronized (this) { try { int sum = 0; for (int i = 1; i <= 10000000; i++) { sum += i; } System.out.println(Thread.currentThread().getName() + ", sum=" + sum); } finally { latch.countDown(); } } } }
package com.oy; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class TestCallable { public static void main(String[] args) throws Exception { Demo d = new Demo(); // 执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收结果 FutureTask<Integer> result = new FutureTask<>(d); new Thread(result).start(); // 接收线程运算后的结果 // 线程没有返回结果前,result.get() 处于等待 Integer sum = result.get(); // FutureTask 可用于闭锁 System.out.println(Thread.currentThread().getName() + ", sum=" + sum); } } class Demo implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 1; i <= 10000000; i++) { sum += i; } System.out.println(Thread.currentThread().getName() + ", sum=" + sum); return sum; } }
解决多线程安全问题的方式
package com.oy; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { Ticket t = new Ticket(); new Thread(t, "1 号窗口").start(); new Thread(t, "2 号窗口").start(); new Thread(t, "3 号窗口").start(); } } class Ticket implements Runnable { private int tick = 100; private Lock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock(); // 加锁 if (tick > 0) { try { Thread.sleep(200); } catch (Exception e) { } System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + (--tick)); } } finally { lock.unlock(); // 释放锁 } } } }
7、
---
标签:代码块 ons 之间 关键字 同步锁 asn override task 系统
原文地址:https://www.cnblogs.com/xy-ouyang/p/12832077.html