标签:arraylist 原子性 类变量 div tin 返回 关联 count 无效
并发编程主要设计两个关键字:一个是synchronized,另一个是volatile。下面主要讲解这两个关键字,并对这两个关机进行比较。
synchronized
synchronized是通过JMV种的monitorenter和monitorexit指令实现同步。monitorenter指令是在编译后插入到同步代码的开始位置,而monitorexit插入到同步代码的结束位置和异常位置。每一个对象都与一个monitor相关联,当monitor被只有后,它将处于锁定状态。
当一个线程试图访问同步代码时,它必须先获得锁;退出或者抛出异常时,必须释放锁。Java中,每一个对象都可以作为锁。具体的表现形式有3种:
权限修饰符 synchronized 返回值类型 函数名(形参列表..){ //函数体 }
权限修饰符 static synchronized 返回值类型 函数名(形参列表..){ //函数体 }
Synchronized(锁){ //需要同步的代码块 }
注意:在同步代码块/同步方法中调用sleep()不会释放锁对象,调用wait()会释放锁对象
Synchronized提供了一种排他式的数据同步机制,某个线程在获取monitor lock的时候可能会被阻塞,而这种阻塞有两个明显的缺陷:1. 无法控制阻塞时长; 2. 阻塞不能被中断
public class SyncDefect { /** *线程休眠一个小时 */ public synchronized void syncMethod(){ try { TimeUnit.HOURS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { SyncDefect defect = new SyncDefect(); new Thread(defect::syncMethod,"t1").start(); //休眠3毫秒后启动线程t2,确保t1先进入同步方法 TimeUnit.MILLISECONDS.sleep(3); Thread t2 = new Thread(defect::syncMethod, "t2"); t2.start(); //休眠3毫秒后中断线程t2,确保t2已经启动 TimeUnit.MILLISECONDS.sleep(3); t2.interrupt(); System.out.println(t2.isInterrupted()); //true System.out.println(t2.getState()); //BLOCKED } }
针对synchronized的两个缺点,可以使用BooleanLock来解决
public interface Lock { void lock() throws InterruptedException; /** * 指定获取锁的超时时间 * @param mills 等待获取锁的最大时间 * @throws InterruptedException * @throws TimeoutException */ void lock(long mills) throws InterruptedException, TimeoutException; void unlock(); List<Thread> getBlockedThreads(); }
public class BooleanLock implements Lock { /** * 记录取得锁的线程 */ private Thread currentThread; /** * Bollean开关,标志锁是否已经被获取 */ private boolean locked = false; private List<Thread> blockedList = new ArrayList<>(); @Override public void lock() { //使用同步代码块的方式获取锁 synchronized (this) { Thread currentThread = Thread.currentThread(); //当锁已经被某个线程获取,将当前线程加入阻塞队列,并使用this.wait()释放thisMonitor while (locked){ try { if(!blockedList.contains(currentThread)){ blockedList.add(currentThread); } this.wait(); } catch (InterruptedException e) { blockedList.remove(currentThread); e.printStackTrace(); } } blockedList.remove(currentThread); this.locked = true; this.currentThread = currentThread; } } @Override public void lock(long mills) throws InterruptedException, TimeoutException { synchronized (this){ if(mills <= 0) {//时间不合法,调用默认的lock() this.lock(); } else { long remainingMills = mills; long endMills = System.currentTimeMillis() + remainingMills; while (locked) { if (remainingMills <= 0) {//在指定的时间内未获取锁或者当前线程被其它线程唤醒,抛出异常 throw new TimeoutException(Thread.currentThread().getName()+" can‘t get lock during "+mills); } if(!blockedList.contains(Thread.currentThread())){ blockedList.add(Thread.currentThread()); } //等待remainingMills后重新尝试获取锁 this.wait(remainingMills); remainingMills = endMills - System.currentTimeMillis(); } blockedList.remove(Thread.currentThread()); this.locked = true; this.currentThread = Thread.currentThread(); } } } @Override public void unlock() { synchronized (this) { if(Thread.currentThread() == currentThread) { this.locked = false; this.notifyAll(); } } } @Override public List<Thread> getBlockedThreads() { return Collections.unmodifiableList(blockedList); } }
/** * 测试阻塞中断 */ public class BooleanLockInterruptTest { private final Lock lock = new BooleanLock(); public void syncMethod() { try { lock.lock(); System.out.println(Thread.currentThread().getName()+" get lock."); TimeUnit.HOURS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("BLOCKED THREAD :"+lock.getBlockedThreads()); lock.unlock(); } } public static void main(String[] args) throws InterruptedException { BooleanLockInterruptTest test = new BooleanLockInterruptTest(); new Thread(test::syncMethod,"t1").start(); TimeUnit.MILLISECONDS.sleep(3); Thread t2 = new Thread(test::syncMethod, "t2"); t2.start(); TimeUnit.MILLISECONDS.sleep(3); t2.interrupt(); System.out.println(t2.isInterrupted()); //true System.out.println(t2.getState()); //RUNNABLE } }
/** * 测试超时 */ public class BooleanLockTimeOutTest { private final Lock lock = new BooleanLock(); public void syncTimeOutMethod() { try { lock.lock(1000); System.out.println(Thread.currentThread().getName()+" get lock."); TimeUnit.HOURS.sleep(1); } catch (InterruptedException | TimeoutException e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { BooleanLockTimeOutTest test = new BooleanLockTimeOutTest(); new Thread(test::syncTimeOutMethod,"t1").start(); TimeUnit.MILLISECONDS.sleep(3); new Thread(test::syncTimeOutMethod, "t2").start(); } }
针对是synhronized还有一些概念及相关知识点需要补充
public synchronized void transfer(int from , int to, double amount) throws InterruptedException{ while(accounts[from] < amount){ //wait on intrinsic object lock’s single condition wait(); } accounts[from] -= amount; accounts[to] += amount; //notify all threads waiting on the condition notifyAll(); }
volatile
volatile是轻量级的synchronized,它为实例域的同步访问提供了一种免锁机制,不会引起线程上下文的切换和调度。它在多处理器开发中保证了共享变量的“可见性“,如果一个属性被声明成volatile,Java模型会确保所有的线程看到这个变量的值时一致的。【volatile变量不能提供原子性】
volatile主要用来锁住一个属性,在对该属性的值进行写操作时,会将数据写回主存,并将CPU里缓存了该内存地址的数据无效。【线程在对volatile修饰的变量进行读写操作时,会首先检查线程缓存的值是否失效,如果失效,就会从主存中把数据读到线程缓存里】。 volatile本质上就是告诉JVM当前变量的值需要从主存中读取,当前变量的值被修改后直接刷新到主存中,且不需要被编译器优化(即:禁止命令重排)。
使用示范:
private volatile boolean done; public boolean isDone(){ return done; } public void setDone(boolean done){ this.done = done; } // Same as private boolean done; public synchronized boolean isDone(){ return done; } public synchronized void setDone(boolean done){ this.done = done; }
比较synchronized和volatile
|
volatile
|
synchronized
|
作用对象
|
实例变量、类变量
|
方法、代码块
|
原子性
|
不具备
|
具备
|
可见性
|
具备
|
具备
|
可见性原理
|
使用机器指令的方式迫使其它工作内存中的变量失效
|
利用monitor锁的排它性实现
|
是否会指令重排
|
否
|
是
|
是否造成线程阻塞
|
否
|
是
|
并发编程之关键字(synchronized、volatile)
标签:arraylist 原子性 类变量 div tin 返回 关联 count 无效
原文地址:https://www.cnblogs.com/BlueStarWei/p/11703653.html