java多线程的一些感悟

来源:互联网 发布:故宫淘宝h5 编辑:程序博客网 时间:2024/05/01 09:03

举个例子吧, 比如说有一个仓库(资源池对象),仓库里存放了20把镰刀(资源池含有20个资源),现在有100个人(100个线程)想要到仓库里拿镰刀去割草(获取资源做相关的事情)。他们同时来到了仓库门前(并发),想要打开仓库门去拿镰刀,但是仓库每次只能进1个人,于是仓库管理员(JVM)就提供了一把锁(synchonized关键字),想要进去的人拿着这把锁把门锁上,然后后面的人就进不去了(线程互斥)。一个人拿完镰刀出来,就把锁给下一个人,下一个人重复前一个人的动作,锁仓库拿镰刀,直到20把镰刀都拿完了(资源用尽)。接下来,剩下的80个人他们不知道仓库里还有多少把镰刀,还是重复前面人的动作,锁住仓库,进去一看,镰刀没了,他们就出来,站在小角落里等其他人用完镰刀还回来(wait()方法)。好了,80个人都在等,终于有人割完草回来了,他回来把仓库锁住,把镰刀放进去,出来时他有两种方式来告诉在等的人说,有一个镰刀已经还回来了。一种方法是只告诉一个人(notify()),还一种是告诉所有人(notifyAll())。如果只告诉一个人,那个人就锁仓库拿镰刀,没有问题。 如果告诉所有人,每个人都依次进去拿镰刀,第一个人OK,第二个人就拿不到了,所以需要判断一下仓库里有没有镰刀,不能盲目去拿,不然会出事(抛出异常)。如果有人在拿了镰刀的时候在仓库里睡觉(sleep),外面的人也没办法,只有傻等等他出来。这就是sleepwait的区别。sleep占着茅坑不拉屎,我在里面不出来;wait比较好,看见没有我要的东西,我出来,我把锁给你们,你们自己进去看(sleep不释放锁,wait释放锁)。这是大集体时代,100个人只有一个仓库(有指向同一个对象的引用)。假设到了现代,每个人都有一个仓库,那么当一个人锁住自己的仓库的时候,其他人还是可以进自己的仓库(所有线程共享一个变量   VS 每个线程含有一个类的对象)。所以说如果每个线程初始化的时候都是new了一个资源池对象,那么你再怎么给资源上锁,还是对其它线程没影响。所以用synchronized 修饰方法实际上是给对象上锁。有几个对象就有几个仓库,大家互不干扰(当然如果每个线程都包含对同一个对象的引用,那还是有影响的,因为只有一个仓库嘛)。但是如果类里面有静态变量,就相当于给类上锁,也就是给类的所有实例对象上锁,这样就对其它线程产生了影响。所以写程序的时候要想清楚,是对同一个对象进行多线程操作,还是像socket一样来一个连接,我启动一个新线程,每个连接互不干扰。

多线程还有一个关键词volatile。当我们用这个修饰一个变量时,每次在读取这个变量的时候都会读取这个变量最新的值。最新是因为每个JVM线程可能会对这个变量保存一个本地副本,加了这个词,其它的线程能够看到除自己之外的线程对这个变量做了什么操作。其实synchronized关键词除了也有这个功能外,还有一个功能就是对对象上锁从而达到互斥(上面例子的锁仓库)。

以上是我在实现一个资源池时参考资料对常见的多线程的一些感悟。附上我写的一个简单模拟资源池的例子。

[java]view plaincopyprint?
  1. import java.util.Stack;  
  2. publicclass MyResource {  
  3. publicint fooNumber = 0;  
  4. privateint totalNumber = 20;  
  5. privatevolatile Stack idleFooStack = new Stack();  
  6. public MyResource(){  
  7. for(int i = 0; i < totalNumberispan>
  8.             idleFooStack.push(new Integer(0));  
  9.         }  
  10.     }  
  11. @SuppressWarnings("static-access")  
  12. public  Integer getResource(String tname, Thread t) throws InterruptedException{  
  13.         Integer i = null;  
  14. synchronized(idleFooStack){  
  15. if(idleFooStack.isEmpty()){  
  16. while(true){  
  17.                     System.out.println(tname + " is waiting");  
  18.                     idleFooStack.wait();//说明线程是为了idleFooStack而等待,可以这么理解,notifyAll()之后,等待的线程接着往下执行,默认还是会得到锁
  19. if(!idleFooStack.isEmpty()){//因为是notifyAll(),如果不加此判断,会出现空栈pop()
  20.                         System.out.println(tname + " get a resource, sleep 5s");  
  21.                         t.sleep(5000);//为了说明sleep跟wait的区别,在这种情况下,这个线程会一直持有这一段代码的锁,其它被唤醒的线程只能继续等待
  22.                         i = idleFooStack.pop();  
  23. break;  
  24.                     }  
  25.                 }     
  26.             }else{  
  27.                 i = idleFooStack.pop();  
  28.             }  
  29.         }  
  30. return i;  
  31.     }  
  32. publicvoid realease(Integer i){  
  33. synchronized(idleFooStack){  
  34.             i = 0;  
  35.             idleFooStack.push(i);  
  36.             idleFooStack.notifyAll();//通知所有因为idleFooStack而等待的线程,当然也可以用notify(),只不过用notifyAll()需要更加小心,见getResource()处理逻辑
  37.         }  
  38.     }  
  39. }  
这是线程池例子。

[java]view plaincopyprint?
  1. publicclass TestThread extends Thread {  
  2. public String name;  
  3. public Integer resource;  
  4.     MyResource pool;  
  5. public TestThread(MyResource pool, String name){  
  6. this.name = name;  
  7. this.pool = pool;  
  8.     }  
  9. publicvoid run(){  
  10. try {  
  11.             resource = pool.getResource(name, this);  
  12.             Thread.sleep(5000);  
  13.             pool.realease(resource);  
  14.             System.out.println(name + " released a resource");  
  15.         } catch (InterruptedException e) {  
  16.             e.printStackTrace();  
  17.         }  
  18.     }  
  19. }  
这是线程例子

[java]view plaincopyprint?
  1. publicclass Test {  
  2. /**
  3.      * @param args
  4.      */
  5. publicstaticvoid main(String[] args) {  
  6.         MyResource resource = new MyResource();  
  7. for(int i = 0; i < span>50; i++){  
  8.             TestThread t = new TestThread(resource, "t" + i);  
  9.             t.start();  
  10.         }  
  11.     }  
  12. }  

这是测试代码
0 0