Chinaunix首页 | 论坛 | 博客
  • 博客访问: 533442
  • 博文数量: 135
  • 博客积分: 3568
  • 博客等级: 中校
  • 技术积分: 1942
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-19 17:52
文章分类

全部博文(135)

文章存档

2012年(29)

2011年(41)

2010年(26)

2009年(12)

2008年(9)

2007年(12)

2006年(6)

分类: Java

2011-03-14 11:44:19

上学学Java的时候,多线程的安全问题是必讲的,不过ThreadLocal这个概念却没有提及,虽然简单,但还是做个笔记吧。
在Java中,一个变量的作用域有几种:
1. 方法内部的定义的变量:
       被称之为局部变量,仅在当前方法内可访问。
2. private 成员变量:
       只能当前类的所有代码可以访问。
3. protected 成员变量:
       当前类代码或者子类代码可以访问。
4. 没有权限关键字修饰的成员变量:
       当前package下的所有代码可以访问,子类可以。
5. public 成员变量:
       所有代码中均可访问。
6. static 变量:
       同一个类的所有实例(对象)共享同一个对象引用
7. ThreadLocal 成员变量:
       多个线程之间保存独立的变量(即同一个类,同一个对象可以在不同的线程中拥有线程独立的成员变量。)

小结:
多线程如果共用同一个对象:
1. 如果该对象的成员变量要在线程间共享,则需要使用synchronized关键字适当的进行加锁。
2. 但是如果不共享,则需要使用ThreadLocal对象。

 TestThreadLocal.zip  


MyService.java
  1. package me.test;

  2. public interface MyService {
  3.     String execute();
  4. }
MyThread.java
  1. package me.test;

  2. public class MyThread extends Thread {

  3.     private MyService service = null;

  4.     public MyThread(String name) {
  5.         super(name);
  6.     }

  7.     @Override
  8.     public void run() {
  9.         while (true) {
  10.             String rtn = service.execute();
  11.             System.out.println("Thread[" + this.getName() + "] :" + rtn);
  12.         }
  13.     }

  14.     public void setService(MyService service) {
  15.         this.service = service;
  16.     }
  17. }


MyServiceImpl1.java
  1. package me.test;

  2. import java.util.Random;

  3. // 这是一个错误的例子
  4. // 示例输出:
  5. //Thread[AAA] :0 -> 1
  6. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :0 -> 2 ERROR
  7. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :2 -> 3
  8. //Thread[AAA] :1 -> 4 ERROR
  9. //Thread[AAA] :4 -> 5
  10. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :3 -> 6 ERROR
  11. //Thread[AAA] :5 -> 7 ERROR
  12. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :6 -> 8 ERROR
  13. //Thread[AAA] :7 -> 9 ERROR
  14. //Thread[AAA] :9 -> 10
  15. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :8 -> 11 ERROR
  16. //Thread[AAA] :10 -> 12 ERROR
  17. //Thread[AAA] :12 -> 13
  18. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :11 -> 14 ERROR
  19. //Thread[AAA] :13 -> 15 ERROR
  20. public class MyServiceImpl1 implements MyService {
  21.     private int num = 0;
  22.     private static Random r = new Random(System.currentTimeMillis());

  23.     @Override
  24.     public String execute() {
  25.         int preVal = num;

  26.         // 用Sleep来代替实际业务中可能会出现的耗时操作,从而更容易发生多线程错误
  27.         int sleepTime = 100 + r.nextInt(900);
  28.         try {
  29.             Thread.sleep(sleepTime);
  30.         } catch (InterruptedException e) {
  31.             e.printStackTrace();
  32.         }

  33.         num++;
  34.         return preVal + " -> " + num + (num - preVal != 1 ? " ERROR" : "");
  35.     }

  36.     public static void main(String[] args) {
  37.         MyService service = new MyServiceImpl1();
  38.         MyThread aaa = new MyThread("AAA");
  39.         aaa.setService(service);
  40.         aaa.start();

  41.         MyThread bbb = new MyThread("BBBBBBBBBBBBBBBBBBBBBBBBBB");
  42.         bbb.setService(service);
  43.         bbb.start();
  44.     }
  45. }

MyServiceImpl2.java
  1. package me.test;

  2. import java.util.Random;

  3. // 这是一个正确的例子,
  4. // 变量在线程之间是不共享的
  5. // 每个线程中的num都是从0开始,每次增1。
  6. // 示例输出:
  7. //Thread[AAA] :0 -> 1
  8. //Thread[AAA] :1 -> 2
  9. //Thread[AAA] :2 -> 3
  10. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :0 -> 1
  11. //Thread[AAA] :3 -> 4
  12. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :1 -> 2
  13. //Thread[AAA] :4 -> 5
  14. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :2 -> 3
  15. //Thread[AAA] :5 -> 6
  16. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :3 -> 4
  17. //Thread[AAA] :6 -> 7
  18. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :4 -> 5
  19. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :5 -> 6
  20. //Thread[AAA] :7 -> 8
  21. public class MyServiceImpl2 implements MyService {

  22.     // 使用ThreadLocal
  23.     private ThreadLocal<Integer> num = new ThreadLocal<Integer>();
  24.     private static Random r = new Random(System.currentTimeMillis());

  25.     @Override
  26.     public String execute() {
  27.         if (null == num.get()) {
  28.             num.set(0);
  29.         }
  30.         int preVal = num.get();

  31.         // 用Sleep来代替实际业务中可能会出现的耗时操作,从而更容易发生多线程错误
  32.         int sleepTime = 100 + r.nextInt(900);
  33.         try {
  34.             Thread.sleep(sleepTime);
  35.         } catch (InterruptedException e) {
  36.             e.printStackTrace();
  37.         }

  38.         num.set(num.get() + 1);
  39.         return preVal + " -> " + num.get()
  40.                 + (num.get() - preVal != 1 ? " ERROR" : "");
  41.     }

  42.     public static void main(String[] args) {
  43.         MyService service = new MyServiceImpl2();
  44.         MyThread aaa = new MyThread("AAA");
  45.         aaa.setService(service);
  46.         aaa.start();

  47.         MyThread bbb = new MyThread("BBBBBBBBBBBBBBBBBBBBBBBBBB");
  48.         bbb.setService(service);
  49.         bbb.start();
  50.     }
  51. }


MyServiceImpl3.java
  1. package me.test;

  2. import java.util.Random;

  3. // 这是一个正确的例子,
  4. // 变量在线程之间是共享的
  5. // 所有线程共建一个自增序列,线程被序列化了(排队),
  6. // 每个线程必须等到队列前面的所有Thread都执行完毕,
  7. // 才能执行被加锁的代码(这里是整个execute()方法)
  8. // 示例输出:
  9. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :0 -> 1
  10. //Thread[AAA] :1 -> 2
  11. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :2 -> 3
  12. //Thread[AAA] :3 -> 4
  13. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :4 -> 5
  14. //Thread[AAA] :5 -> 6
  15. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :6 -> 7
  16. //Thread[AAA] :7 -> 8
  17. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :8 -> 9
  18. //Thread[AAA] :9 -> 10
  19. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :10 -> 11
  20. //Thread[AAA] :11 -> 12
  21. //Thread[BBBBBBBBBBBBBBBBBBBBBBBBBB] :12 -> 13
  22. public class MyServiceImpl3 implements MyService {
  23.     private int num = 0;
  24.     private static Random r = new Random(System.currentTimeMillis());

  25.     // 如果变量在多线程之间确实要共享,则必须使线程同步(synchronized)
  26.     // 这里使用的是方法级别的 synchronize,可以根据业务实际缩小 synchronize 的范围。
  27.     @Override
  28.     public synchronized String execute() {
  29.         int preVal = num;

  30.         // 用Sleep来代替实际业务中可能会出现的耗时操作,从而更容易发生多线程错误
  31.         int sleepTime = 100 + r.nextInt(900);
  32.         try {
  33.             Thread.sleep(sleepTime);
  34.         } catch (InterruptedException e) {
  35.             e.printStackTrace();
  36.         }

  37.         num++;
  38.         return preVal + " -> " + num + (num - preVal != 1 ? " ERROR" : "");
  39.     }

  40.     public static void main(String[] args) {
  41.         MyService service = new MyServiceImpl3();
  42.         MyThread aaa = new MyThread("AAA");
  43.         aaa.setService(service);
  44.         aaa.start();

  45.         MyThread bbb = new MyThread("BBBBBBBBBBBBBBBBBBBBBBBBBB");
  46.         bbb.setService(service);
  47.         bbb.start();
  48.     }
  49. }





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