网站在线开放端口,网站开发难题,湖北省住房与建设厅网站,免费咨询电脑维修在CSDN开了博客后#xff0c;一直也没在上面公布过文章#xff0c;直到前一段时间与一位前辈的对话#xff0c;才发现技术博客的重要#xff0c;立志要把CSDN的博客建好。但一直没有找到好的开篇的主题#xff0c;今天再看JAVA线程相互排斥、同步的时候又有了新的体会一直也没在上面公布过文章直到前一段时间与一位前辈的对话才发现技术博客的重要立志要把CSDN的博客建好。但一直没有找到好的开篇的主题今天再看JAVA线程相互排斥、同步的时候又有了新的体会就以他作为开篇吧。 在JAVA中是没有类似于PV操作、进程相互排斥等相关的方法的。JAVA的进程同步是通过synchronized()来实现的须要说明的是JAVA的synchronized()方法类似于操作系统概念中的相互排斥内存块在JAVA中的Object类型中都是带有一个内存锁的在有线程获取该内存锁后其他线程无法訪问该内存从而实现JAVA中简单的同步、相互排斥操作。明确这个原理就能理解为什么synchronized(this)与synchronized(static XXX)的差别了synchronized就是针对内存区块申请内存锁thiskeyword代表类的一个对象所以其内存锁是针对同样对象的相互排斥操作而static成员属于类专有其内存空间为该类全部成员共同拥有这就导致synchronized()对static成员加锁相当于对类加锁也就是在该类的全部成员间实现相互排斥在同一时间仅仅有一个线程可訪问该类的实例。假设仅仅是简单的想要实如今JAVA中的线程相互排斥明确这些基本就已经够了。但假设须要在线程间相互唤醒的话就须要借助Object.wait(), Object.nofity()了。 Obj.wait()与Obj.notify()必需要与synchronized(Obj)一起使用也就是wait,与notify是针对已经获取了Obj锁进行操作从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后主动释放对象锁同一时候本线程休眠。直到有其他线程调用对象的notify()唤醒该线程才干继续获取对象锁并继续运行。对应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后并非立即就释放对象锁的而是在对应的synchronized(){}语句块运行结束自己主动释放锁后JVM会在wait()对象锁的线程中随机选取一线程赋予其对象锁唤醒线程继续运行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都能够暂停当前线程释放CPU控制权基本的差别在于Object.wait()在释放CPU同一时候释放了对象锁的控制。 单单在概念上理解清楚了还不够须要在实际的样例中进行測试才干更好的理解。对Object.wait()Object.notify()的应用最经典的样例应该是三线程打印ABC的问题了吧这是一道比較经典的面试题题目要求例如以下 建立三个线程A线程打印10次AB线程打印10次B,C线程打印10次C要求线程同一时候执行交替打印10次ABC。这个问题用Object的wait()notify()就能够非常方便的解决。代码例如以下 public class MyThreadPrinter2 implements Runnable { private String name; private Object prev; private Object self; private MyThreadPrinter2(String name, Object prev, Object self) { this.name name; this.prev prev; this.self self; } Override public void run() { int count 10; while (count 0) { synchronized (prev) { synchronized (self) { System.out.print(name); count--; self.notify(); } try { prev.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws Exception { Object a new Object(); Object b new Object(); Object c new Object(); MyThreadPrinter2 pa new MyThreadPrinter2(A, c, a); MyThreadPrinter2 pb new MyThreadPrinter2(B, a, b); MyThreadPrinter2 pc new MyThreadPrinter2(C, b, c); new Thread(pa).start();new Thread(pb).start();new Thread(pc).start(); }
} 先来解释一下其总体思路从大的方向上来讲该问题为三线程间的同步唤醒操作基本的目的就是ThreadA-ThreadB-ThreadC-ThreadA循环运行三个线程。为了控制线程运行的顺序那么就必需要确定唤醒、等待的顺序所以每个线程必须同一时候持有两个对象锁才干继续运行。一个对象锁是prev就是前一个线程所持有的对象锁。另一个就是自身对象锁。基本的思想就是为了控制运行的顺序必需要先持有prev锁也就前一个线程要释放自身对象锁再去申请自身对象锁两者兼备时打印之后首先调用self.notify()释放自身对象锁唤醒下一个等待线程再调用prev.wait()释放prev对象锁终止当前线程等待循环结束后再次被唤醒。运行上述代码能够发现三个线程循环打印ABC共10次。程序运行的主要过程就是A线程最先运行持有C,A对象锁后释放A,C锁唤醒B。线程B等待A锁再申请B锁后打印B再释放BA锁唤醒C线程C等待B锁再申请C锁后打印C再释放C,B锁唤醒A。看起来似乎没什么问题但假设你细致想一下就会发现有问题就是初始条件三个线程依照A,B,C的顺序来启动依照前面的思考A唤醒BB唤醒CC再唤醒A。可是这样的假设依赖于JVM中线程调度、运行的顺序。详细来说就是在main主线程启动ThreadA后需要在ThreadA运行完在prev.wait()等待时再切回线程启动ThreadBThreadB运行完在prev.wait()等待时再切回主线程启动ThreadC仅仅有JVM依照这个线程运行顺序运行才干保证输出的结果是正确的。而这依赖于JVM的详细实现。考虑一种情况例如以下假设主线程在启动A后运行A过程中又切回主线程启动了ThreadB,ThreadC之后因为A线程尚未释放self.notify也就是B需要在synchronized(prev)处等待而这时C却调用synchronized(prev)获取了对b的对象锁。这样在A调用完后同一时候ThreadB获取了prev也就是a的对象锁ThreadC的运行条件就已经满足了会打印C之后释放c,及b的对象锁这时ThreadB具备了运行条件会打印B也就是循环变成了ACBACB了。这样的情况能够通过在run中主动释放CPU来进行模拟。代码例如以下 public void run() { int count 10; while (count 0) { synchronized (prev) { synchronized (self) { System.out.print(name); count--; try{Thread.sleep(1);}catch (InterruptedException e){e.printStackTrace();}self.notify(); } try { prev.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } 执行后的打印结果就变成了ACBACB了。为了避免这样的与JVM调度有关的不确定性。须要让A,B,C三个线程以确定的顺序启动终于代码例如以下
public class MyThreadPrinter2 implements Runnable { private String name; private Object prev; private Object self; private MyThreadPrinter2(String name, Object prev, Object self) { this.name name; this.prev prev; this.self self; } Override public void run() { int count 10; while (count 0) { synchronized (prev) { synchronized (self) { System.out.print(name); count--; try{Thread.sleep(1);}catch (InterruptedException e){e.printStackTrace();}self.notify(); } try { prev.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws Exception { Object a new Object(); Object b new Object(); Object c new Object(); MyThreadPrinter2 pa new MyThreadPrinter2(A, c, a); MyThreadPrinter2 pb new MyThreadPrinter2(B, a, b); MyThreadPrinter2 pc new MyThreadPrinter2(C, b, c); new Thread(pa).start();Thread.sleep(10);new Thread(pb).start();Thread.sleep(10);new Thread(pc).start();Thread.sleep(10);}
} 这样才干够完美的解决该问题。通过这个样例也是想说明一下非常多理论、概念如Obj.wait(),Obj.notify()等理解起来比較简单可是在实际的应用其中这里却是往往出现故障的地方。须要更加深入的理解。并在解决这个问题的过程中不断加深对概念的掌握。 ——欢迎转载请注明出处 http://blog.csdn.net/zyplus—— 转载于:https://www.cnblogs.com/zfyouxi/p/4516295.html