关于JAVA多线程同步

因为需要,最近关注了一下JAVA多线程同步问题。JAVA多线程同步主要依赖于若干方法和关键字。将心得记录如下:


1 wait方法: 该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所在的代码块的锁,并在其他线程调用notify或者notifyAll方法时恢复到竞争锁状态(一旦获得锁就恢复执行)。 调用wait方法需要注意几点: 第一点:wait被调用的时候必须在拥有锁(即synchronized修饰的)的代码块中。 第二点:恢复执行后,从wait的下一条语句开始执行,因而wait方法总是应当在while循环中调用,以免出现恢复执行后继续执行的条件不满足却继续执行的情况。 第三点:若wait方法参数中带时间,则除了notify和notifyAll被调用能激活处于wait状态(等待状态)的线程进入锁竞争外,在其他线程中interrupt它或者参数时间到了之后,该线程也将被激活到竞争状态。 第四点:wait方法被调用的线程必须获得之前执行到wait时释放掉的锁重新获得才能够恢复执行。2 notify方法和notifyAll方法: notify方法通知调用了wait方法,但是尚未激活的一个线程进入线程调度队列(即进入锁竞争),注意不是立即执行。并且具体是哪一个线程不能保证。另外一点就是被唤醒的这个线程一定是在等待wait所释放的锁。 notifyAll方法则唤醒所有调用了wait方法,尚未激活的进程进入竞争队列。3 synchronized关键字: 第一点:synchronized用来标识一个普通方法时,表示一个线程要执行该方法,必须取得该方法所在的对象的锁。 第二点:synchronized用来标识一个静态方法时,表示一个线程要执行该方法,必须获得该方法所在的类的类锁。 第三点:synchronized修饰一个代码块。类似这样:synchronized(obj) { //code…. }。表示一个线程要执行该代码块,必须获得obj的锁。这样做的目的是减小锁的粒度,保证当不同块所需的锁不冲突时不用对整个对象加锁。利用零长度的byte数组对象做obj非常经济。4 atomic action(原子操作): 在JAVA中,以下两点操作是原子操作。但是c和c++中并不如此。 第一点:对引用变量和除了long和double之外的原始数据类型变量进行读写。 第二点:对所有声明为volatile的变量(包括long和double)的读写。 另外:在java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依赖于同步机制的线程安全的类和方法。5 一个例子,该例子模仿多人存取同一个账户:Account类:package com.synchronize;import java.util.HashMap;import java.util.Iterator;public class Account { private static HashMap<String, Integer> m = new HashMap<String, Integer>(); private static long times = 0; static { m.put("ren", 1000); } public synchronized void save(String name, int num) { long tempTime = times++; System.out.println("第 " + tempTime + " 次存储" + num + "之前" + name + "的余额为:" + m.get(name)); m.put(name, m.get(name) + num); this.notify(); System.out.println("第 " + tempTime + " 次存储" + num + "之后" + name + "的余额为:" + m.get(name)); } public static int get(String name) { return m.get(name); } /** * 注意wait的用法,必须在loop中,必须在拥有锁的代码块中。 前者是当被notify的时候要重新进行条件判断,后者是为了释放锁。 * * @param name * @param num */ public synchronized void load(String name, int num) { long tempTime = times++; System.out.println("第 " + tempTime + " 次提取" + num + "之前" + name + "的余额为:" + m.get(name)); try { while (m.get(name) < num) { System.out.println("第 " + tempTime + " 次提取" + "余额" + m.get(name) + "不足,开始等待wait。"); this.wait(); System.out.println("第 " + tempTime + " 次提取操作被唤醒"); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } m.put(name, m.get(name) – num); System.out.println("第 " + tempTime + " 次提取" + num + "之后" + name + "的余额为:" + m.get(name)); }}User类:package com.synchronize;/*** 这里注意runnable接口的线程是怎么实例化的。new Thread(new User())* 这里成功展示了多个用户存取同一个账户的多线程实例,通过多线程同步,保证了安全的执行。* @author abc**/public class User implements Runnable { private static Account account = new Account(); private final int id; User(int i){ id=i; } public void run() { int tempMoney = 100; account.load("ren", tempMoney); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } account.save("ren", 100); System.out.println("线程"+id+"完毕========================================================"); } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new User(i)).start(); } }}


后续:请额外关注 ThreadLocal、JDK 5 中增加的 Lock 接口

。1.Lock接口java.utils.concurrent包是jdk1.5新增的,用来处理多线程。实现java.util.concurrent.locks.Lock接口的类具有与synchronized关键字同样的功能,但是它更加强大一些。java.utils.concurrent.locks.ReentrantLock是较常用的实现了Lock接口的类。下面是ReentrantLock类的一个应用实例:

    packageedu.hust.test;importjava.util.concurrent.locks.ReentrantLock;publicclassThreadLockextendsReentrantLock{privatestaticfinallongserialVersionUID=1L;publicvoidexecute_lock1(){this.lock();try{for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+"位于/"同步块/"中"+i);Thread.sleep(999999999);}}catch(InterruptedExceptione){System.out.println("谁把我吵醒了…..");}finally{this.unlock();}}publicsynchronizedvoidexecute_lock2(){//lock.lock();try{for(inti=0;i<5;i++)System.out.println(Thread.currentThread().getName()+"位于/"同步块/"中"+i);}finally{//lock.unlock();}}publicstaticvoidmain(String[]args){finalThreadLockthreadLock=newThreadLock();Threadthread1=newThread(newRunnable(){publicvoidrun(){threadLock.execute_lock1();}},"红薯Thread");Threadthread2=newThread(newRunnable(){publicvoidrun(){threadLock.execute_lock1();}},"土豆Thread");Threadthread3=newThread(newRunnable(){publicvoidrun(){threadLock.execute_lock2();}},"青菜Thread");thread1.start();thread2.start();thread3.start();}/**输出结果:*红薯Thread位于"同步块"中0*青菜Thread位于"同步块"中0*青菜Thread位于"同步块"中1*青菜Thread位于"同步块"中2*青菜Thread位于"同步块"中3*青菜Thread位于"同步块"中4**/}

lock()方法用于锁定对象,unlock()方法用于释放对对象的锁定,他们都是在Lock接口中定义的方法。位于这两个方法之间的代码在被执行时,效果等同于被放在synchronized同步块中。一般用法是将需要在lock()和unlock()方法之间执行的代码放在try{}块中,并且在finally{}块中调用unlock()方法,这样就可以保证即使在执行代码抛出异常的情况下,对象的锁也总是会被释放,否则的话就会为死锁的产生增加可能。和synchronized关键字不同,lock只把当前方法锁定,其他的方法(不论是同步还是非同步的)并没有被锁定。2.死锁死锁就是一个进程中的每个线程都在等待这个进程中的其他线程释放所占用的资源,从而导致所有线程都无法继续执行的情况。死锁是多线程编程中一个隐藏的陷阱,它经常发生在多个线程共用资源的时候。在实际开发中,死锁一般隐藏的较深,不容易被发现。程序中必须同时满足以下四个条件才会引发死锁: (1)互斥(Mutual exclusion):线程所使用的资源中至少有一个是不能共享的,它在同一时刻只能由一个线程使用。 (2)持有与等待(Hold and wait):至少有一个线程已经持有了资源,并且正在等待获取其他的线程所持有的资源。 (3)非抢占式(No pre-emption):如果一个线程已经持有了某个资源,那么在这个线程释放这个资源之前,别的线程不能把它抢夺过去使用。 (4)循环等待(Circular wait):假设有N个线程在运行,第一个线程持有了一个资源,并且正在等待获取第二个线程持有的资源,而第二个线程正在等待获取第三个线程持有的资源,依此类推……第N个线程正在等待获取第一个线程持有的资源,由此形成一个循环等待。

    packageedu.hust.test;/***@authorForrestHe*createdon2008-11-17*/publicclassDeadLockDemoimplementsRunnable{inti;//用于判断线程是否被占用/**必须是static的,static表示锁住的是Object类;而非static锁住的是Object的对象,在这里不符合要求.**/privatestaticObjectprint_one=newObject();//第一台打印机privatestaticObjectprint_two=newObject();//第二台打印机publicvoidrun(){if(i==0){//先占用print_one,并等待print_twosynchronized(print_one){System.out.println("print_one已被占用,正等待print_two");try{Thread.sleep(1000);}catch(InterruptedExceptione){System.err.println("谁把我吵醒了!!!");}synchronized(print_two){System.out.println("已获取print_two,任务完成");}}}elseif(i==1){//先占用print_two,并等待print_onesynchronized(print_two){System.out.println("print_two已被占用,正等待print_one");try{Thread.sleep(1000);}catch(InterruptedExceptione){System.err.println("谁把我吵醒了!!!");}synchronized(print_one){System.out.println("已获取print_one,任务完成");}}}}publicstaticvoidmain(String[]args){DeadLockDemodeadLock_one=newDeadLockDemo();DeadLockDemodeadLock_two=newDeadLockDemo();deadLock_one.i=0;deadLock_two.i=1;newThread(deadLock_one).start();newThread(deadLock_two).start();}}

躲在墙角、掩藏那孤独而又不奢怜悯的伤…

关于JAVA多线程同步

相关文章:

你感兴趣的文章:

标签云: