Chinaunix首页 | 论坛 | 博客
  • 博客访问: 152131
  • 博文数量: 47
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 405
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-23 14:38
文章分类

全部博文(47)

文章存档

2017年(7)

2016年(4)

2015年(19)

2014年(17)

我的朋友

分类: Java

2015-10-29 23:43:18

熟练掌握多线程是成为中高级程序员的必备技能。多线程的功能和意义是很明确的:为了提高吞吐量,提高资源利用率,提高CPU效率。比如你要从一个设备获取数据,然后对数据进行处理。如果只用一个线程从前到后执行,当采集数据IO时cpu是空闲的,当处理数据时又无法IO,这样效率太低,这时就要用到多线程。所以高级程序语言都有专门的针对多线程的套路。
多线程中最复杂的问题就是解决共享冲突问题,假设有两个线程,都对同一个变量进行操作,如何让操作变的有规矩,而不发生冲突。最常见的例子就是银行账户问题,一个账户有1000元,多个ATM机同时对一个账户进行取1000元钱操作,就可能存在一个账户取完了钱,另一个账户仍然可以取钱,因为它判断的时候账户余额还是1000。又或者试想你右手拿着筷子想夹一块肉,这块肉却突然被另一个人吃掉了。
JAVA里提供synchronized关键字解决多线程共享问题。
如:

点击(此处)折叠或打开

  1. class Account {
  2.  
  3.     String name;
  4.     float amount;
  5.     
  6.     public Account(String name,float amount){
  7.         this.name=name;
  8.         this.amount=amount;
  9.     }
  10.     public synchronized void deposit(float amt){
  11.         float tmp = amount;
  12.         tmp+=amt;
  13.         try {
  14.             Thread.sleep(1);
  15.         } catch (InterruptedException e) {
  16.             // TODO Auto-generated catch block
  17.             e.printStackTrace();
  18.         }
  19.         amount = tmp;
  20.     }
  21.     public synchronized void withdraw(float amt){
  22.         float tmp = amount;
  23.         tmp-=amt;
  24.         try {
  25.             Thread.sleep(1);
  26.         } catch (InterruptedException e) {
  27.             // TODO Auto-generated catch block
  28.             e.printStackTrace();
  29.         }
  30.         amount=tmp;
  31.     }
  32.     public float getBalance(){
  33.         return amount;
  34.     }
  35. }

  36. public class Test{
  37.     private static int NUM_OF_THREAD = 1000;
  38.     static Thread[] threads = new Thread[NUM_OF_THREAD];
  39.     
  40.     public static void main(String[] args){
  41.         final Account acc = new Account("John", 1000.0f);
  42.         long time1 = System.currentTimeMillis();
  43.         for (int i = 0; i< NUM_OF_THREAD; i++) {
  44.             threads[i] = new Thread(new Runnable() {
  45.                 public void run() {
  46.                         acc.deposit(100.0f);
  47.                         acc.withdraw(100.0f);
  48.                 }
  49.             });
  50.             threads[i].start();
  51.         }

  52.         for (int i=0; i<NUM_OF_THREAD; i++){
  53.             try {
  54.                 threads[i].join(); //等待所有线程运行结束
  55.             } catch (InterruptedException e) {
  56.                 // ignore
  57.             }
  58.         }
  59.         long time2 = System.currentTimeMillis();
  60.         System.out.println("Finally, John's balance is:" + acc.getBalance()+"go on time:"+(time2-time1)+"ms");
  61.     }
  62. }
输出结果为:Finally, John's balance is:1000.0go on time:2117ms

点击(此处)折叠或打开

  1. class Account {
  2.  
  3.     String name;
  4.     float amount;
  5.     
  6.     public Account(String name,float amount){
  7.         this.name=name;
  8.         this.amount=amount;
  9.     }
  10.     public void deposit(float amt){
  11.         float tmp = amount;
  12.         tmp+=amt;
  13.         try {
  14.             Thread.sleep(1);
  15.         } catch (InterruptedException e) {
  16.             // TODO Auto-generated catch block
  17.             e.printStackTrace();
  18.         }
  19.         amount = tmp;
  20.     }
  21.     public void withdraw(float amt){
  22.         float tmp = amount;
  23.         tmp-=amt;
  24.         try {
  25.             Thread.sleep(1);
  26.         } catch (InterruptedException e) {
  27.             // TODO Auto-generated catch block
  28.             e.printStackTrace();
  29.         }
  30.         amount=tmp;
  31.     }
  32.     public float getBalance(){
  33.         return amount;
  34.     }
  35. }

  36. public class Test{
  37.     private static int NUM_OF_THREAD = 1000;
  38.     static Thread[] threads = new Thread[NUM_OF_THREAD];
  39.     
  40.     public static void main(String[] args){
  41.         final Account acc = new Account("John", 1000.0f);
  42.         long time1 = System.currentTimeMillis();
  43.         for (int i = 0; i< NUM_OF_THREAD; i++) {
  44.             threads[i] = new Thread(new Runnable() {
  45.                 public void run() {
  46.                         acc.deposit(100.0f);
  47.                         acc.withdraw(100.0f);
  48.                 }
  49.             });
  50.             threads[i].start();
  51.         }

  52.         for (int i=0; i<NUM_OF_THREAD; i++){
  53.             try {
  54.                 threads[i].join(); //等待所有线程运行结束
  55.             } catch (InterruptedException e) {
  56.                 // ignore
  57.             }
  58.         }
  59.         long time2 = System.currentTimeMillis();
  60.         System.out.println("Finally, John's balance is:" + acc.getBalance()+"go on time:"+(time2-time1)+"ms");
  61.     }
  62. }
去掉synchronized输出结果为:Finally, John's balance is:8800.0go on time:318ms(这个输出结果是随机的)
从上面可以看出,使用synchronized可以使线程按序进行,它是这么实现的:当一个线程使用synchronized方法时首先会获得该对象的锁,然后执行完代码再释放锁,其他线程在它释放锁之前无法获得该锁就无法执行synchronized方法,这样就保证了每时每刻只有一个线程才可以执行synchronized方法。同时从执行时间来看,使用synchronized后会拖延线程的执行速度的。这就要求我们不要把过多的操作放在一个synchronized方法中,可以采用synchronized块解决问题。

点击(此处)折叠或打开

  1. class Foo extends Thread
  2. {
  3.     private String name;
  4.     private String val;
  5.     public Foo(String name,String v)
  6.     {
  7.     this.name=name;
  8.     val = v;
  9.     }
  10.     public void printVal()
  11.     {
  12.     synchronized(val) {
  13.     while(true) System.out.println(name+val);
  14.     }
  15.     }
  16.     public void run()
  17.     {
  18.     printVal();
  19.     }
  20. }
  21. public class Test
  22. {
  23.     public static void main(String args[])
  24.     {
  25.     Foo f1 = new Foo("Foo 1:","printVal");
  26.     f1.start();
  27.     Foo f2 = new Foo("Foo 2:","printVal");
  28.     f2.start();
  29.     }
  30. }
输出结果:Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal(无限)
用synchronized锁住了string对象val,线程1占据对象的锁,一直不放开,线程2就无法执行该代码块。从这里可以看出synchronized锁住的是一个类的对象,而使用这个对象的同步方法时就需要先获得锁。


阅读(1182) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~