java 线程同步

来源:互联网 发布:神武2mac版能玩吗 编辑:程序博客网 时间:2024/06/07 11:06

这个问题是我最近在做实习笔试的时候遇到的,当时很水的 用了同步块搞定了,不过现在还是决定稍微深入一点。


总的来说线程同步就是通过加锁和解锁机制来实现的,如果存在2个线程对同一个变量的访问,特别是出现删改的操作,这时候就必须对变量进行加锁,限制并发访问的线程数量。我对这个概念的了解始于操作系统,我记得这里面存在一个临界区的概念。临界区是涉及到线程共享资源操作的区域的总体,对于共享资源的操作需要以串行形式存在,当然为了访问性能,读操作可以多线程,但是读写必须分离,简单说就是同一个时刻只能有至多一个线程来操作临界区中线程的共享资源。

之前在C#中实现过线程同步,java中的线程同步好像没有那么好用,(ps 基于我目前接触到的方面)。


首先一个最粗暴的方式就是直接同步关键字

给方法加上synchronized,   这样同一个对象的该方法同时只有一个线程能调用,这个算是最简单粗暴的吧。


然后到了同步代码块

public class Main {Object metrux;int applecount;public Main() {metrux = new Object();applecount = 0;}public static void main(String[] args) {// TODO Auto-generated method stubMain main = new Main();main.startThread();}public void startThread() {PutApple putApple = new PutApple();GetApple getApple = new GetApple();Thread putThread = new Thread(putApple);Thread getThread = new Thread(getApple);putApple.start();getApple.start();}class PutApple extends Thread {public void run() {//boolean isFull = false;while (true) {synchronized (metrux) {if (applecount < 5) {applecount++;System.out.println("Put a apple.now have " + applecount+ " apple(s)");if(applecount == 1){metrux.notify();}} else {//isFull = true;System.out.println("Package full");    try {metrux.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}//if(isFull){//Thread.yield();//}try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}class GetApple extends Thread {public void run() {//boolean isEmpty =false;while (true) {synchronized (metrux) {if (applecount > 0) {applecount--;System.out.println("Get a apple.now have " + applecount+ " apple(s)");if(applecount == 4){metrux.notify();}} else {//isEmpty = true;System.out.println("Package Empty");try {metrux.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}try {Thread.sleep(300);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}//if(isEmpty){//Thread.yield();//}}}}}

上面的 extends  Thread 可以无视  没有用的,   同步代码块是用synchronized (object){}  的结构对代码区进行加锁,因为java中每个对象都有一个对象锁,这个语句就是请求对象的对象锁,同时只能有一个代码块能获得这个锁,这样也就保证了临界区访问的正确性。

在代码中我尝试了  wait 和notify的方法,   wait方法是让线程退出同步块 同时释放锁,并且在被唤醒之前  不去获得新的该对象的锁,

                                  notify 方法是让线程退出同步块,同时释放锁,并且随机唤醒一个在这个对象上等待的线程


基于这样的原则 我们可以做到类似循环打印的事情。

package concurrent;//会出现死锁  在多核情况下 不稳定public class PrintABC implements Runnable {private String name;private Object prev;private Object self;private PrintABC(String name, Object prev, Object self) {this.name = name;this.prev = prev;this.self = self;}@Overridepublic 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();PrintABC pa = new PrintABC("A", c, a);PrintABC pb = new PrintABC("B", a, b);PrintABC pc = new PrintABC("C", b, c);new Thread(pa).start();Thread.sleep(1);new Thread(pb).start();Thread.sleep(1);new Thread(pc).start();Thread.sleep(1);}}
在我们预期的情况下  我们会认为打印出ABCABC。。。。这样的结构,但是这个在多核的情况下实际上时不稳定的,任何顺序都可能出现,同时存在一个问题,比如 A线程开始,拿到C锁,之后主线程开启B线程,B线程拿到A锁,主线程开启C线程,C线程拿到A锁,这样的情况下就会出现死锁,虽然几率小,但是问题还是存在。


这时候最后就要引入信号量这个类,目前我还没有深入了解,我目前就当成了PV操作里的锁机制,以及C#中的同步锁。Semaphore这个类,可以实现获得锁,释放锁的操作,并且可以控制获得锁的对象的个数,这个在很多情况下有很大价值,现在就放一个用信号量改进的循环打印ABC的多线程同步demo


package concurrent;import java.util.concurrent.Semaphore;public class TestSemaphore {Semaphore semaphoreA = new Semaphore(1);Semaphore semaphoreB = new Semaphore(1);Semaphore semaphoreC = new Semaphore(1);public static void main(String[] args) {TestSemaphore testSemaphore = new TestSemaphore();testSemaphore.test();} public void test() {try {semaphoreB.acquire();semaphoreC.acquire();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}Thread printA = new Thread(new Print("A", semaphoreA, semaphoreB));Thread printB = new Thread(new Print("B", semaphoreB, semaphoreC));Thread printC = new Thread(new Print("C", semaphoreC, semaphoreA));printA.start();printB.start();printC.start();}class Print implements Runnable {String name;Semaphore mutex;Semaphore mutexnext;public Print(String name, Semaphore mutex, Semaphore mutexnext) {this.name = name;this.mutex = mutex;this.mutexnext = mutexnext;}public void run() {int i = 10;while (i-- > 0) {try {mutex.acquire();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("this is : " + name);mutexnext.release();}}}}


我目前的了解就到这里,欢迎大神带来意见和新的方法途径。



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 红米手机前置摄像头用不了怎么办 红米4a手机电池不耐用怎么办 红米6全网通联通网络不好怎么办 红米手机死机了怎么办不可拆卸电池 厦华电视指示灯亮但打不开机怎么办 oppo一体机的开机键坏了怎么办 小米手机长时间没用开不了机怎么办 红米2a充电坏了怎么办? 红米手机恢复出厂设置失败怎么办 红米关机强行恢复出厂失败怎么办 红米2a太卡了怎么办 红米2a上网好卡怎么办 红米1内部存储空间坏了怎么办 红米3s开关机键失灵怎么办 红米3s下面三个键失灵怎么办 红米3s手机掉水怎么办 红米手机用久了卡怎么办 红米4x手机不支持计步怎么办 红米4x手机耗电快怎么办 红米4a一体机手机死机怎么办 红米4x打王者卡怎么办 红米5 4g信号不稳定怎么办 红米3x玩游戏卡顿怎么办 红米3开不了机了怎么办 苹果手机装了sim卡没反应怎么办 小米手机打电话的图标没了怎么办 租房时和房东没签协议装修怎么办 三星安卓手机忘记锁屏密码怎么办 刷机了支付宝的余额宝钱没了怎么办 手机刷机支付宝里面的钱怎么办 支付宝宽带缴费交错账号了怎么办 电信宽带反回到翼支付里的钱怎么办 天猫盒子连接电视没反应怎么办 淘宝定制单发货之后是空物流怎么办 微信购买虚拟物品卖家不发货怎么办 虚拟商品确认收货后申请退款怎么办 手机换号码了淘宝怎么办又不能上网 美团酒店订单取消超时了怎么办 订单中快递单号填错了怎么办 高考动态口令卡页面找不到了怎么办 支付宝收钱码被别人扫了怎么办