Java线程同步 (synchronized wait notify)

来源:互联网 发布:sql一对多查询join 编辑:程序博客网 时间:2024/05/01 01:24

出处 http://blog.csdn.net/weizhaozhe/article/details/3922647


同步(阻塞) :是一种防止对共享资源访问导致的数据不一致的一种模式。

详细请参看操作系统。

在Java中,由于对多线程的支持,对同步的控制主要通过以下几个方法,synchronized,和wait(),notify()和notifyAll(),下面进行一一的讲解:

A关键字synchronized

每个java对象都有一把锁, 当有多个线程同时访问共享资源的时候, 需要Synchronize 来控制安全性, synchronize 分 synchronize 方法 和synchronize块,使用synchronize块时, 一定要显示的获得该对象的锁(如synchronize(object))而方法则不需要。

java的内存模型是对每一个进程有一个主内存, 每个线程有自己的内存, 他们从主内存中取数据, 然后计算, 再存入主内存中。 

并发问题如下:如果多个线程同事操作同一数据, A线程从主内存中取的I的值为1, 然后进行加1操作, 这时B线程也取I的值, 进行加2操作, 然后A存入2到主内存中, B也存入, 这样就覆盖了A的值(同数据库中的并发问题一样)。

解决办法是用synchronize, 如用synchronized(I)。被synchronize 修饰的方法(块)把以下三步操作当成一个原子操作:取数据, 操作数据, 存数据。 我们知道原子操作是不可以被打断的, 所以其保证了数据一致性, 这样同一时间只有一个线程再执行, 对性能有一定的影响。这也是synchronize的第二个作用:保证统一时间只有一个线程再运行。 当实现SOCKET连接的时候经常用到.

JAVA中规定对非FLOAT, LONG的原始类型的取和存操作为原子操作。 其实就是对一个字(32位)的取,存位原始操作, 因为FLOAT, LONG为两个字节的长度, 所以其取, 存为非原子操作。 如果想把他们也变为原子操作, 可以用VOLATILE关键字来修饰

使用方法:

作用区域主要有两种:

1.方法

2.代码块

被synchronized声明的方法被称为同步方法,被其修饰的代码块称为同步语句。无论是同步方法还是同步语句,只要声明为同步了,在同一时刻,同一个对象的同步XX是不可以被同时访问的,而不同对象之间的同步方法是互不干扰的。

具体实现(如下代码都在某个类定义中):

同步方法:

Public synchronized void change() {

//

}

同步语句:(因为效率问题,有时考虑使用同步语句块)

    Public void change() {

Synchronized(this) {

}

}

这个同步语句是针对当前对象的,有时,我们就是想让一段代码同步,可能与当前对象并没什么关系,可以自定义同步的锁。如下:

private byte[]  lock= new byte[0];

 

  Public void change() {

Synchronized(lock) {

}

}

自定义锁注意事项:

1必须是private,防止在类外部引用改变。

2如果可能用到,重写get方法,返回对象的clone,而不是本身。

其他用法:

Synchronized除了可以作用于方法,代码块,还可以作用于静态方法,类,某个实例。但是都存在效率问题,一定要慎用。

Class Foo   {   

public synchronizedstatic void methodAAA()// 同步的static 函数  

{   

//….  

}  

  public void methodBBB()   {   

synchronized(Foo.class)// class literal(类名称字面常量) 

 } }

这样修饰后代表的是:统一时刻,被修饰部分只有一个对象可以运行,因为它的声明是针对类的。

2.wait()/notify()/notifyAll()

注意:

在Java中,每个对象都有个对象锁标志(Object lock flag)与之想关联,当一个线程A调用对象的一段synchronized代码时,

  它首先要获取与这个对象关联的对象锁标志,然后执行相应的代码,执行结束后,把这个对象锁标志返回给对象;因此,在线程A执行

  synchronized代码期间,如果另一个线程B也要执行同一对象的一段synchronized代码时(不一定与线程A执行的相同),它将

  要等到线程A执行完后,才能继续....

  

  如何利用wait() notify() notifyAll()?

  

  在synchronized代码被执行期间,线程可以调用对象的wait()方法,释放对象锁标志,进入等待状态,并且可以调用notify()或者

  notifyAll()方法通知正在等待的其他线程。notify()通知等待队列中的第一个线程,notifyAll()通知的是等待队列中的所有线程

例子程序:

/**

 *PrintNum.java

 * Created on 5:18:04 PM Feb 22, 2009

 *@author Quasar063501

 *@version 0.1

 * 

 */

public class PrintNum {

private byte[] lock = new byte[0];  //自定义锁对象,这样代价最小,也可已使用当前对象this

public void demo() {

PrintThread a = new PrintThread("a");

PrintThread b = new PrintThread("b");

a.start();

b.start();

}

class PrintThread extends Thread {

public PrintThread(String name) {

this.setName(name);

}

public void run() {

synchronized(lock) {

for(int i =0; i < 100; i++) {

if(i % 10 == 0 && 0 != i) {

try {

lock.wait();   //暂时释放资源

lock.notify();       //唤醒另外一个进程

catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(this.getName()+": "+i);

}

}

}

}

}

这个程序最终会因为互相唤醒而死锁,请你解决!

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 布的收纳箱有味怎么办 车漆清漆层掉了怎么办 副驾驶储物箱卡子断了怎么办 玛莎拉蒂车门打不开怎么办 新买的水杯漏水怎么办 泰迪小狗掉毛怎么办 手机jlc调用接口状态异常怎么办 进门和厕所对着怎么办 p过的照片有竖条怎么办 当照片p出竖条纹怎么办 月子里落下脚心怕风怕凉怎么办 鞋胶把手粘住了怎么办 凉鞋魔术贴长了怎么办 新买的狗一直叫怎么办 刚买的幼犬老叫怎么办 狗狗什么都不吃怎么办 新买的吊扇风小怎么办 夜市卖果汁没电怎么办 榻榻米太长2米45怎么办 木质桌子黏黏的怎么办 白色塑料桌子染色了怎么办 3dmax模型变透明了怎么办 刚养的兔子不吃怎么办 熊猫兔不吃下喝怎么办 熊猫兔感冒了一直打喷嚏怎么办 兔子后腿骨断了怎么办 兔子的腿肿了怎么办 仓鼠喝了牛奶该怎么办 宠物兔不吃不喝怎么办 兔子把木屑吃了怎么办? 小车司机碰瓷大车司机怎么办 在淘宝买到假的护肤品怎么办 淘宝购物发现是假的怎么办 电脑键盘灯不亮不能打字怎么办 海棠兔屁股有屎怎么办 大冒险告白被接受了怎么办剧透微博 大冒险被告白了怎么办 腐书网 大冒险告白被接受了怎么办 006 大冒险告白被接受了怎么办广播剧 coolpad手机解锁图案忘了怎么办 装死兔严重掉毛怎么办