java为什么要线程同步?Java多线程初学者指南(9):为什么要进行数据同步
大家好,关于java为什么要线程同步很多朋友都还不太明白,今天小编就来为大家分享关于Java多线程初学者指南(9):为什么要进行数据同步的知识,希望对各位有所帮助!
java同步中,为什么要wait,又notify谁
对象锁与同步块或者实例同步方法相关系,但如果线程进入静态同步方法,就必须获得类锁。用锁只能达到这样的目的:使得一个任务不会干涉另一个任务的资源,保证在任何时刻都只有一个任务可以访问某个资源。但两个任务要协同作战,互相通信,要一起工作去解决某个问题,必须使他们友好握手、共商国事。这种机制靠Object的方法wait()和notify()来安全地实现。在Thread对象上调用wait()方法将释放线程所有的锁定,这种说法是错误的。Thread类对象也是对象也有wait()方法,它释放的只是它自己作为线程对象的锁,这在线程池的概念级上理解。似乎理解起来,wait()是自己停止,等待被唤醒;notify()也是自己停止,通知别人。那么感觉没什么大的区别,不急,先仔细分析他们的来历。wait()通常线程要执行下去需要等待某个条件发生变化,但改变这个条件已经超出了当前方法的控制能力。通常,这种条件由另一个任务来改变。既然执行不下去,傻等,又改变不了现实,那还不如交出执行权,令当前线程挂起,同步资源解锁,使别的线程可以访问并修改共享资源,自己进行排队队列,等候别人的通知。经过测试,好象是先入后出的顺序被唤醒的。释放了锁意味着另一个任务可以获得这个锁,这一点至关重要,因为这些其他的方法再入处理通常会引起wait()感兴趣的变化。wait()和notify()必须包括在synchronized代码块中,等待中的线程必须由notify()方法显式地唤醒,否则它会永远地等待下去。很多人初级接触多线程时,会习惯把wait()和notify()放在run()方法里,一定要谨记,这两个方法属于某个对象,应在对象所在的类方法中定义它,然后run中去调用它。这里不得不提下,在Object的wait方法是重载的。有三个方法,了解一下除无参之外的另一个方法wait(毫秒数 n);这里毫秒数是指,如果没有notify通知的情况下,当前被wait线程,经过n毫秒之后依然可以回到可运行状态。如果参数为零,则不考虑实际时间,在获得通知前该线程将一直等待。wait(0, 0)与 wait(0)相同。 notify()唤醒正在队列中等待资源的优先级最高的线程。但它自己不马上退出资源,继续执行,等全部执行完了,退出,释放锁,这样才让wait()的线程进入。所以说在对象(当前线程具有其锁定)调用notify()方法一定释放锁定是只是一厢情愿的。至于与notifyAll()区别,后者更加安全。使用notify(),在众多等待同一个锁的任务中只有一个会被唤醒,因此如果你希望使用notify(),就必须保证被唤醒的是恰当的任务。notify()也就是this.notify(),唤醒所有争抢自己的线程,与别的对象产生的wait()没有关系。 synchronized(a){
System.out.println("notify");
a.notifyAll();//假如这里是wait(),下句代码就暂时不会执行!
System.out.println("continue");//notifyAll()以后,这句代码还是要执行的
}我曾经自己写过如下非常幼稚的代码,写在public void run()里边,目的是在zoneRectangleSize< 1的情况下,使自己的线程处于阻塞状态,虽然不会出错,但这个wait调用的是线程类对象本身的wait(),毕竟它也是来自Object,所以肯定达不到预期的效果: synchronized(mainApp){
} Thread的静态方法sleep()是不释放锁的,也不用操作锁,所以可以在非同步控制方法和run方法内部调用。也就是说当前线程即使进入sleep状态也抱着这把锁睡觉,保持高度的监控状态,即使其它线程在外边踢门叫嚷骂娘,他是心安理得,坚决不释放。如果一直昏睡下去,拥有同一对象资源的线程们都会玩完的。
Java线程安全和同步到底是怎么一回事
1、java中线程同步了也就满足安全性。
2、web工程中每一个浏览器发出的http请求到达服务器的时候,服务器都会建立一个新的线程来处理请求,一般如果写的action中很少会有静态的变量。
a。如果没有静态变量(静态变量都是所有action中唯一的,这些变量属于类,而不是属于某一个实例,也就是说静态变量是实例间共享的),那就不用关心线程安全的问题。
b。如果有静态变量的话,就需要关心线程安全问题了。假设你的类中的方法会对静态变量进行操作。还有一种情况是你的类方法中如果对数据库进行操作的话为了保证操作的原子性则需要在方法上加上关键字synchronized关键字就可以了。
Java多线程初学者指南(9):为什么要进行数据同步
Java中的变量分为两类局部变量和类变量局部变量是指在方法内定义的变量如在run方法中定义的变量对于这些变量来说并不存在线程之间共享的问题因此它们不需要进行数据同步类变量是在类中定义的变量作用域是整个类这类变量可以被多个线程共享因此我们需要对这类变量进行数据同步
数据同步就是指在同一时间只能由一个线程来访问被同步的类变量当前线程访问完这些变量后其他线程才能继续访问这里说的访问是指有写操作的访问如果所有访问类变量的线程都是读操作一般是不需要数据同步的
那么如果不对共享的类变量进行数据同步会发生什么情况呢?让我们先看看下面的代码会发生什么样的事情
packagetest;publicclassMyThreadextendsThread{publicstaticintn=;publicvoidrun(){intm=n;yield();m++;n=m;}publicstaticvoidmain(String[]args)throwsException{MyThreadmyThread=newMyThread();Threadthreads[]=newThread[ ];for(inti=;i<threads length;i++)threads[i]=newThread(myThread);for(inti=;i<threads length;i++)threads[i] start();for(inti=;i<threads length;i++)threads[i] join();System out println( n=+MyThread n);}}
在执行上面代码的可能结果如下
n=
看到这个结果可能很多读者会感到奇怪这个程序明明是启动了个线程然后每个线程将静态变量n加最后使用join方法使这个线程都运行完后再输出这个n值按正常来讲结果应该是n=可偏偏结果小于
其实产生这种结果的罪魁祸首就是我们经常提到的脏数据而run方法中的yield()语句就是产生脏数据的始作俑者(不加yield语句也可能会产生脏数据但不会这么明显只有将改成更大的数才会经常产生脏数据在本例中调用yield就是为了放大脏数据的效果) yield方法的作用是使线程暂停也就是使调用yield方法的线程暂时放弃CPU资源使CPU有机会来执行其他的线程为了说明这个程序如何产生脏数据我们假设只创建了两个线程 thread和thread由于先调用了thread的start方法因此 thread的run方法一般会先运行当thread的run方法运行到第一行(int m= n)时将n的值赋给m当执行到第二行的yield方法后 thread就会暂时停止执行而当thread暂停时 thread获得了CPU资源后开始运行(之前thread一直处于就绪状态)当thread执行到第一行(int m= n)时由于thread在执行到yield时n仍然是因此 thread中的m获得的值也是这样就造成了thread和thread的m获得的都是在它们执行完yield方法后都是从开始加因此无论谁先执行完最后n的值都是只是这个n被thread和thread各赋了一遍值这个过程如下图如示
也许有人会问如果只有n++会产生脏数据吗?答案是肯定的那么n++只是一条语句又如何在执行过程中将CPU交给其他的线程呢?其实这只是表面现象 n++在被Java编译器编译成中间语言(也叫做字节码)后并不是一条语言让我们看看下面的Java代码将会被编译成什么样的Java中间语言
Java源代码
publicvoidrun(){n++;}
被编译后的中间语言代码
publicvoidrun(){ aload_ dup getfield iconst_ iadd putfield return}
大家可以看到在run方法中只有n++一条语句而在编译后却有条中间语言语句我们并不需要知道这些语句的功能是什么只看一下第和行语句在行是getfield根据它的英文含义可知是要得到某个值因为这里只有一个n所以毫无疑问是要得到n的值而在行的iadd也不难猜测是将这个得到的n值加在行的putfield的含义我想大家可能已经猜出来了它负责将这个加后的n再更新回类变量n说到这可能大家还有一个疑惑执行n++时直接将n加不就行了为什么要如此费周折其实这里涉及到一个Java内存模型的问题
Java的内存模型分为主存储区和工作存储区主存储区保存了Java中所有的实例也就是说在我们使用new来建立一个对象后这个对象及它内部的方法变量等都保存在这一区域在MyThread类中的n就保存在这个区域主存储区可以被所有线程共享而工作存储区就是我们前面所讲的线程栈在这个区域里保存了在run方法以及run方法所调用的方法中定义的变量也就是方法变量在线程要修改主存储区中的变量时并不是直接修改这些变量而是将它们先复制到当前线程的工作存储区在修改完后再将这个变量值覆盖主存储区的相应的变量值
lishixinzhi/Article/program/Java/gj/201311/27623JAVA中线程在什么时候需要同步和互斥
何时需要同步
在多个线程同时访问互斥(可交换)数据时,应该同步以保护数据,确保两个线程不会同时修改更改它。
对于非静态字段中可更改的数据,通常使用非静态方法访问
对于静态字段中可更改的数据,通常使用静态方法访问。
1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
使用锁定还有一些其他危险,如死锁(当以不一致的顺序获得多个锁定时会发生死锁)。甚至没有这种危险,锁定也仅是相对的粗粒度协调机制,同样非常适合管理简单操作,如增加计数器或更新互斥拥有者。如果有更细粒度的机制来可靠管理对单独变量的并发更新,则会更好一些;在大多数现代处理器都有这种机制。
关于本次java为什么要线程同步和Java多线程初学者指南(9):为什么要进行数据同步的问题分享到这里就结束了,如果解决了您的问题,我们非常高兴。