- public interface Runnable {public abstract void run();
- }
- public class Thread implements Runnable {
- /* What will be run. */
- private Runnable target;......
- /**
- * Causes this thread to begin execution; the Java Virtual Machine
- * calls the <code>run</code> method of this thread.
- */
- public synchronized void start() {......
- }......@Override public void run() {
- if (target != null) {
- target.run();
- }
- }......
- }
Thread 类与 Runnable 接口都位于 java.lang 包中。从上面我们可以看出,Runnable 接口中只定义了 run() 方法,Thread 类实现了 Runnable 接口并重写了 run() 方法。当调用 Thread 类的 start() 方法时,实际上 Java 虚拟机就去调用 Thread 类的 run() 方法,而 Thread 类的 run() 方法中最终调用的是 Runnable 类型对象的 run() 方法。
- public class ThreadTest1 extends Thread {@Override public void run() {
- while (true) {
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("thread 1:" + Thread.currentThread().getName());
- }
- }
- public static void main(String[] args) {
- ThreadTest1 thread = new ThreadTest1();
- thread.start();
- } //main end
- }
可以写成内部类的形式,new Thread(){@Override run(...)}.start();
- public class ThreadTest2 implements Runnable {@Override public void run() {
- while (true) {
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("thread 3:" + Thread.currentThread().getName());
- }
- }
- public static void main(String[] args) {
- ThreadTest2 thread3 = new ThreadTest2();
- Thread thread = new Thread(thread3);
- thread.start();
- } //main end
- }
可以写成内部类的形式,new Thread(new Runnable(){@Override run(...)}).start();
当调用 Thread 类的 start() 方法时,将会创建一个线程,这时刚创建的线程处于就绪状态(可运行状态),并没有运行,处于就绪状态的线程就可以等 JVM 调度。当 JVM 调度该线程时,该线程进入运行状态,即执行 Thread 类的 run() 方法中的内容。run() 方法执行完,线程结束,线程进入死亡状态。这是线程自然终止的过程,我们也可以通过 Thread 类提供的一些方法来终止线程。
stop() 方法没有做任何的清除操作就粗暴终止线程,释放该线程所持有的对象锁(下文将介绍),受该对象锁保护的其它对象对其他线程可见,因此具有不安全性。
suspend() 方法会使目标线程会停下来,但仍然持有在这之前获得的对象锁,对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。
终上所述,不建议使用 stop() 方法和 suspend() 方法来终止线程,通常我们通过 interrupt() 方法来终止处于阻塞状态和运行状态的线程。
需要注意的是,interrupt() 方法不会中断一个正在运行的线程,仅仅是将线程的中断标记设为 true,当调用了阻塞方法之后,线程会不断监听中断标志,如果为 true,则产生一个 InterruptedException 异常,将 InterruptedException 放在 catch 中就能终止线程。
isInterrupted() 方法可以返回中断标记,常用循环判断条件。
interrupted() 方法测试当前线程是否已经中断,线程的中断标志由该方法清除。interrupted() 除了返回中断标记之外,它还会清除中断标记。
看下面例子
- public class ThreadInterruptedTest extends Thread {@Override public void run() {
- try {
- int i = 0;
- while (!isInterrupted()) {
- i++;
- Thread.sleep(1000);
- System.out.println(this.getName() + " is looping,i=" + i);
- }
- } catch(InterruptedException e) {
- System.out.println(this.getName() + " catch InterruptedException,state:" + this.getState());
- e.printStackTrace();
- }
- }
- public static void main(String[] args) throws Exception {
- ThreadInterruptedTest thread = new ThreadInterruptedTest();
- System.out.println(thread.getName() + " state:" + thread.getState());
- thread.start();
- System.out.println(thread.getName() + " state:" + thread.getState());
- Thread.sleep(5000);
- System.out.println("flag: " + thread.isInterrupted());
- //发出中断指令
- thread.interrupt();
- System.out.println("flag: " + thread.isInterrupted());
- System.out.println(thread.getName() + " state:" + thread.getState());
- System.out.println(thread.interrupted());
- }
- }
运行结果
- Thread - 0 state: NEW Thread - 0 state: RUNNABLE Thread - 0 is looping,
- i = 1 Thread - 0 is looping,
- i = 2 Thread - 0 is looping,
- i = 3 Thread - 0 is looping,
- i = 4 flag: false flag: true Thread - 0 state: TIMED_WAITING Thread - 0
- catch InterruptedException,
- state: RUNNABLE false java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.itpsc.thread.ThreadInterruptedTest.run(ThreadInterruptedTest.java: 11)
从运行结果可以看出,调用 interrupt() 发出中断指令前,中断标志位 false,发出中断指令后中断标志位为 true,而调用 interrupted() 方法后则中断标志被清除。从发出的异常来看,是在一个 sleep interrupted,且发出异常后线程被唤醒,以便线程能从异常中正常退出。
线程从创建到终止可能会经历各种状态。在...State 类的源码中,可以看到线程有以下几种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。各种状态的转换如下:
当通过 Thread t = new Thread() 方式创建线程时,线程处于新建状态;当调用 t.start() 方法时,线程进入可运行状态(注意,还没有运行);处于可运行状态的线程将在适当的时机被 CPU 资源调度器调度,进入运行状态,也就是线程执行 run() 方法中的内容;run() 方法执行完或者程序异常退出线程进入终止状态。线程从运行状态也有可能进入阻塞状态,如调用 wait() 方法后进入等待对象锁(下文将介绍),调用 sleep() 方法后进行入计时等待。
现在我们已经知道线程的创建与终止了。互斥,是指系统中的某些共享资源,一次只允许一个线程访问,当一个线程正在访问该临界资源时,其它线程必须等待。
在 java 中,每一个对象有且仅有一个锁,锁也称为对象监视器。通过对象的锁,多个线程之间可以实现对某个方法(临界资源)的互斥访问。那么,如何获取对象的锁呢?当我们调用对象的 synchronized 修饰的方法或者 synchronized 修饰的代码块时,锁住的是对象实例,就获取了该对象的锁。
Java 中有实例对象也有类对象,竟然有对象锁,那么久有类锁,也称全局锁。当 synchronized 修饰静态方法或者静态代码块时,锁住的是该类的 Class 实例(字节码对象),获取的便是该类的全局锁。看下面获取对象锁实现线程互斥的两种方式。
先看下面这个没有实现线程互斥的例子。
- public class SynchronizedTest {
- public static void main(String[] args) {
- new SynchronizedTest().init();
- }
- private void init() {
- final Outputer output = new Outputer();
- //线程1打印"hello,i am thread 1"
- new Thread(new Runnable() {@Override public void run() {
- while (true) {
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- output.output("hello,i am thread 1");
- }
- }
- }).start();
- //线程2打印"hello,i am thread 2"
- new Thread(new Runnable() {@Override public void run() {
- while (true) {
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- output.output("hello,i am thread 2");
- }
- }
- }).start();
- }
- class Outputer {
- public void output(String name) {
- for (int i = 0; i) {
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- }
运行结果
- hello,
- i am thread 1 hello,
- i am thread 2 hello,
- i am hellthread 1 o,
- i am thread 2 hello,
- i am thread 2 hello,
- i am thread 1 hello,
- i am thread 2 hello,
- i am threadhel 2lo,
- i am thread 1
线程 1 和线程 2 同时调用进行输出,从运行结果可以看出,线程之间没有执行完各自的输出任务就被交替了运行了。下面通过对象的锁实现线程 1 和线程 2 对 output 方法的互斥访问。
使用 synchronized 对 output 方法进行修饰,可以让调用者获得锁。synchronized 修饰方法没有显示声明锁的对象,默认是当前方法所在类的对象 this。
- public synchronized void output(String name) {
- for (int i = 0; i) {
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
使用 synchronized 对 output 方法中的代码块进行修饰,也可以让调用者获得锁。
- public void output(String name) {
- synchronized(this) {
- for (int i = 0; i) {
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
使用 synchronized 之后,线程 1 和线程 2 对 output 方法实现了互斥访问。
- hello,
- i am thread 1 hello,
- i am thread 2 hello,
- i am thread 1 hello,
- i am thread 2 hello,
- i am thread 1 hello,
- i am thread 2 hello,
- i am thread 1
先看下面的例子,我们来总结下 synchronized 的一些常用用法。
- public class SynchronizedTest {
- public static void main(String[] args) {
- new SynchronizedTest().init();
- }
- private void init() {
- final Outputer output = new Outputer();
- //线程1打印"hello,i am thread 1"
- new Thread(new Runnable() {@Override public void run() {
- output.output("hello,i am thread 1");
- }
- }).start();
- //线程2打印"hello,i am thread 2"
- new Thread(new Runnable() {@Override public void run() {
- output.output("hello,i am thread 2");
- }
- }).start();
- }
- static class Outputer {
- public synchronized void output(String name) {
- for (int i = 0; i < 5; i++) {
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(name);
- }
- }
- public void output2(String name) {
- synchronized(this) {
- for (int i = 0; i < 5; i++) {
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(name);
- }
- }
- }
- public void output3(String name) {
- for (int i = 0; i < 5; i++) {
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(name);
- }
- }
- public static synchronized void output4(String name) {
- for (int i = 0; i < 5; i++) {
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(name);
- }
- }
- public void output5(String name) {
- synchronized(Outputer.class) {
- for (int i = 0; i < 5; i++) {
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(name);
- }
- }
- }
- }
- }
运行结果
- hello,
- i am thread 1 hello,
- i am thread 1 hello,
- i am thread 1 hello,
- i am thread 1 hello,
- i am thread 1 hello,
- i am thread 2 hello,
- i am thread 2 hello,
- i am thread 2 hello,
- i am thread 2 hello,
- i am thread 2
线程 1 和线程 2 同时访问 output 对象的 synchronized 修饰的 output 方法,即两个线程竞争的是 output 对象的锁,这是同一个锁,所以当线程 1 在持有锁的时候,线程 2 必须等待,即下面的用法 1。
用法 1
当一个线程访问某个对象的 synchronized 方法或者 synchronized 代码块时,其它线程对该对象的该 synchronized 方法或者 synchronized 代码块的访问将阻塞。
用法 2
当一个线程访问某个对象的 synchronized 方法或者 synchronized 代码块时,其它线程对该对象的其他 synchronized 方法或者 synchronized 代码块的访问将阻塞。
修该上面的 SynchronizedTest 例子,线程 1 访问 output 方法,线程 2 访问 output2 方法,运行结果同上,因为 output 方法 和 output2 方法都属于同一个对象 output ,因此线程 1 和线程 2 竞争的也是同一个锁。
用法 3
当一个线程访问某个对象的 synchronized 方法或者 synchronized 代码块时,其它线程仍然可以对该对象的其他非 synchronized 方法或者 synchronized 代码块访问。
修该上面的 SynchronizedTest 例子,线程 1 访问 output 方法,线程 2 访问 output3 方法,运行结果是线程 1 和线程 2 交替输出。结果显而易见,线程 2 访问 output3 方法并不是 synchronized 修饰的 output 方法或者代码块,线程 2 并不需要持有锁,因此线程 1 的运行不会阻塞线程 2 的运行。
用法 4
当 synchronized 修饰静态方法时,锁住的是该类的 Class 实例(字节码对象)。修该上面的 SynchronizedTest 例子,线程 1 访问 output4 方法,线程 2 访问 output5 方法,运行结果同用法 1,说明线程 1 和线程 2 竞争的是 Outputer 类的 Class 实例(字节码对象)的锁。
多个线程之间往往需要相互协作来完成某一个任务,synchronized 和对象锁能实现线程互斥,但是不能实现线程通信。
线程之间的通信通过 java.lang 包中 Object 类中的 wait() 方法和 notify()、notifyAll() 等方法进行。我们知道,Java 中每个对象都有一个锁,wait() 方法用于等待对象的锁,notify()、notifyAll() 方法用于通知其他线程对象锁可以使用。
wait()\notify()\notifyAll() 依赖于对象锁,对象锁是对象所持有,Object 类是所有 java 类的父类,这样每一个 java 类(对象)都有线程通信的基本方法。这就是这些方法定义在 Object 类中而不定义在 Thread 类中的原因。
wait() 方法的会让当前线程释放对象锁并进入等待对象锁的状态,当前线程是指正在 cpu 上运行的线程。当前线程调用 notify()\notifyAll() 后,等待对象锁的线程将被唤醒。
调用 wait() 方法或者 notify() 方法的对象必须和对象锁所属的对象是同一个对象,并且必须在 synchronized 方法或者 synchronized 代码块中被调用。
yieId() 的作用是给线程调度器一个提示,告知线程调度器当前线程愿意让出 CPU,但是线程调度器可以忽略这个提示。因此,yieId() 的作用仅仅是告知线程调度器当前线程愿意让出 CPU 给其他线程执行(竟然只是愿意,当前线程可以随时反悔,那其他线程也不一定能得到 CPU 执行),而且不会让当前线程释放对象锁。
yieId() 能让当前线程由运行状态进入到就绪状态,从而让其它具有相同优先级的等待线程获取执行权。但是,并不能保证在当前线程调用 yield() 之后,其它具有相同优先级的线程就一定能获得执行权,也有可能当前线程又进入到运行状态继续运行。
yieId() 只建议在测试环境中使用。
wait() 和 yield() 的区别
(1)wait()是让线程由运行状态进入到等待 (阻塞) 状态,而 yield()是让线程由运行状态进入到就绪状态。
(2)wait() 是让线程释放它所持有对象的锁,而 yield() 方法不会释放锁。
下面的例子是 "主线程输出三次接着子线程输出三次",重复两次。
- public class WaitnotifyTest {
- public static volatile boolean shouldChildren = false;
- public static void main(String[] args) throws Exception {
- final Outputer outputer = new Outputer();
- //创建子线程
- Thread chrild = new Thread(new Runnable() {@Override public void run() {
- try {
- for (int i = 0; i < 2; i++) outputer.children();
- } catch(Exception e) {
- e.printStackTrace();
- }
- }
- });
- chrild.start();
- //主线程
- for (int i = 0; i < 2; i++) outputer.main();
- }
- }
- class Outputer {
- //子线程循环输出
- public synchronized void children() throws Exception {
- while (!WaitnotifyTest.shouldChildren) {
- System.out.println(Thread.currentThread().getName() + " thread end loop,go to waitting");
- //子线程进入等待状态
- this.wait();
- }
- System.out.println(Thread.currentThread().getName() + " thread start loop");
- for (int i = 1; i <= 3; i++) {
- System.out.println("hello,i am chrildren thread,loop:" + i);
- }
- WaitnotifyTest.shouldChildren = false;
- //唤醒主线程
- this.notify();
- }
- //主线程循环输出
- public synchronized void main() throws Exception {
- while (WaitnotifyTest.shouldChildren) {
- System.out.println(Thread.currentThread().getName() + " thread end loop,go to waitting");
- //主线程进入等待状态
- this.wait();
- }
- System.out.println(Thread.currentThread().getName() + " thread start loop");
- for (int i = 1; i <= 3; i++) {
- System.out.println("hello,i am main thread,loop:" + i);
- }
- WaitnotifyTest.shouldChildren = true;
- //唤醒子线程
- this.notify();
- }
- }
运行结果
- main thread start loop hello,
- i am main thread,
- loop: 1 hello,
- i am main thread,
- loop: 2 hello,
- i am main thread,
- loop: 3 main thread end loop,
- go to waitting Thread - 0 thread start loop hello,
- i am chrildren thread,
- loop: 1 hello,
- i am chrildren thread,
- loop: 2 hello,
- i am chrildren thread,
- loop: 3 Thread - 0 thread end loop,
- go to waitting main thread start loop hello,
- i am main thread,
- loop: 1 hello,
- i am main thread,
- loop: 2 hello,
- i am main thread,
- loop: 3 Thread - 0 thread start loop hello,
- i am chrildren thread,
- loop: 1 hello,
- i am chrildren thread,
- loop: 2 hello,
- i am chrildren thread,
- loop: 3
volatile 修饰 shouldChildren,线程直接读取 shouldChildren 变量并且不缓存它,修改了 shouldChildren 立马让其他线程可见,这就确保线程读取到的变量是一致的。
线程本地变量,可能称为线程局部变量更容易理解,即为每一个使用该变量的线程都提供一个变量值的副本,相当于将变量的副本绑定到线程中,每一个线程可以独立地修改自己的变量副本,而不会和其它线程的变量副本冲突。在线程消失之后,线程局部变量的所有副本都会被垃圾回收(下面的源码分析中将提到)。
ThreadLocal
在 java.lang.Thread 类中,有一个 ThreadLocal.ThreadLocalMap 类型的变量 threadLocals,这个变量就是用来存储线程局部变量的。
- /* ThreadLocal values pertaining to this thread. This map is maintained
- * by the ThreadLocal class. */
- ThreadLocal.ThreadLocalMap threadLocals = null;
下面我们重点分析 ThreadLocal 的内部实现。ThreadLocal 也位于 java.lang 包中。其主要成员有:
- public T get() {}
- private T setInitialValue() {}
- public void set(T value) {}
- private void remove(ThreadLocal key) {}
- ThreadLocalMap getMap(Thread t) {}
- void createMap(Thread t, T firstValue) {}
- static class ThreadLocalMap {}
Set
我们从 set 方法开始。Set 方法源码如下
- /**
- * Sets the current thread's copy of this thread-local variable
- * to the specified value. Most subclasses will have no need to
- * override this method, relying solely on the {@link #initialValue}
- * method to set the values of thread-locals.
- *
- * @param value the value to be stored in the current thread's copy of
- * this thread-local.
- */
- public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) map.set(this, value);
- else createMap(t, value);
- }
先获取当前的线程,然后通过 getMap(t) 方法获取到一个 map,map 的类型为 ThreadLocalMap。
这个 map 其实就是存储线程变量的对象 threadLocals。ThreadLocalMap 是 ThreadLocal 中的一个内部类,是一个定制的 hashmap 以便适用于存储线程本地变量。竟然是定制的 hashmap,那么就有 Entry 和 table(hashmap 的内部实现参考上一篇:)。而 ThreadLocalMap 中的 Entry 继承了 WeakReference,弱引用是不能保证不被垃圾回收器回收的,这就是前文提到的在线程消失之后,线程局部变量的所有副本都会被垃圾回收。此外,Entry 中使用 ThreadLocal 作为 key,线程局部变量作为 value。如果 threadLocals 不为空,则设值否者调用 createMap 方法创建 threadLocals。注意设值的时候传的是 this 而不是当前线程 t。
- /**
- * ThreadLocalMap is a customized hash map suitable only for
- * maintaining thread local values. No operations are exported
- * outside of the ThreadLocal class. The class is package private to
- * allow declaration of fields in class Thread. To help deal with
- * very large and long-lived usages, the hash table entries use
- * WeakReferences for keys. However, since reference queues are not
- * used, stale entries are guaranteed to be removed only when
- * the table starts running out of space.
- */
- static class ThreadLocalMap {
- /**
- * The entries in this hash map extend WeakReference, using
- * its main ref field as the key (which is always a
- * ThreadLocal object). Note that null keys (i.e. entry.get()
- * == null) mean that the key is no longer referenced, so the
- * entry can be expunged from table. Such entries are referred to
- * as "stale entries" in the code that follows.
- */
- static class Entry extends WeakReference {
- /** The value associated with this ThreadLocal. */
- Object value;
- Entry(ThreadLocal k, Object v) {
- super(k);
- value = v;
- }
- }
接下来我们看看 createMap 方法
- /**
- * Create the map associated with a ThreadLocal. Overridden in
- * InheritableThreadLocal.
- *
- * @param t the current thread
- * @param firstValue value for the initial entry of the map
- * @param map the map to store.
- */
- void createMap(Thread t, T firstValue) {
- t.threadLocals = new ThreadLocalMap(this, firstValue);
- }
createMap 方法其实就是为当前线程的 threadLocals 变量分配空间并存储线程的第一个变量。现在我们已经知道线程是如何初始化并设值自己的局部变量了,下面我们看看取值。
Get
- /**
- * Returns the value in the current thread's copy of this
- * thread-local variable. If the variable has no value for the
- * current thread, it is first initialized to the value returned
- * by an invocation of the {@link #initialValue} method.
- *
- * @return the current thread's value of this thread-local
- */
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null) return (T) e.value;
- }
- return setInitialValue();
- }
先获取当前的线程,然后通过 getMap(t) 方法获取当前线程存变量的对象 threadLocals,如果 threadLocals 不为空则取值并返回(注意传入的 key 是 this 对象而不是当前线程 t),否则调用 setInitialValue 方法初始化。setInitialValue 和 set 方法唯一不同的是调用了 initialValue 进行初始化,也就是在获取变量之前要初始化。
- /**
- * Variant of set() to establish initialValue. Used instead
- * of set() in case user has overridden the set() method.
- *
- * @return the initial value
- */
- private T setInitialValue() {
- T value = initialValue();
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) map.set(this, value);
- else createMap(t, value);
- return value;
- }
总的来讲,每创建一个线程(Thread 对象),该线程即拥有存储线程本地变量的 threadLocals 对象,threadLocals 对象初始为 null,当通过 ThreadLocal 对象调用 set/get 方法时,就会对线程的 threadLocals 对象进行初始化,并且以当前 ThreadLocal 对象为键值,以 ThreadLocal 要保存的变量为 value,存到 threadLocals。看下面的例子。
View Code
- public class ThreadLocalShareVariable {
- public static void main(String[] args) {
- //创建3个线程
- for (int i = 0; i < 3; i++) {
- //创建线程
- new Thread(new Runnable() {@Override public void run() {
- //线程设置自己的变量
- int age = new Random().nextInt(100);
- String name = getRandomString(5);
- System.out.println("Thread " + Thread.currentThread().getName() + " has put data:" + name + " " + age);
- //存储与当前线程有关的变量
- Passenger.getInstance().setName(name);
- Passenger.getInstance().setAge(age);
- //线程访问共享变量
- new ModuleA().getData();
- new ModuleB().getData();
- }
- }).start();
- }
- }
- static class ModuleA {
- public void getData() {
- //获取与当前线程有关的变量
- String name = Passenger.getInstance().getName();
- int data = Passenger.getInstance().getAge();
- System.out.println("moduleA get data from " + Thread.currentThread().getName() + ":" + name + " " + data);
- }
- }
- static class ModuleB {
- public void getData() {
- //获取与当前线程有关的变量
- String name = Passenger.getInstance().getName();
- int data = Passenger.getInstance().getAge();
- System.out.println("moduleB get data from " + Thread.currentThread().getName() + ":" + name + " " + data);
- }
- }
- /**
- * 随机生成字符串
- * @param length
- * @return
- */
- public static String getRandomString(int length) {
- final String str = "abcdefghijklmnopqrstuvwxyz";
- StringBuffer sb = new StringBuffer();
- int len = str.length();
- for (int i = 0; i < length; i++) {
- sb.append(str.charAt((int) Math.round(Math.random() * (len - 1))));
- }
- return sb.toString();
- }
- }
- class Passenger {
- private String name;
- private int age;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public Passenger() {}
- //ThreadLocal存储线程变量
- public static ThreadLocal thsd = new ThreadLocal();
- public static Passenger getInstance() {
- //获取当前线程范围内的共享变量实例
- Passenger passenger = thsd.get();
- //懒汉模式创建实例
- if (passenger == null) {
- passenger = new Passenger();
- thsd.set(passenger);
- }
- return passenger;
- }
- }
运行结果
View Code
- Thread Thread - 1 has put data: vwozg 33 Thread Thread - 2 has put data: hubdn 30 Thread Thread - 0 has put data: mkwrt 35 moduleA get data from Thread - 2 : hubdn 30 moduleA get data from Thread - 0 : mkwrt 35 moduleA get data from Thread - 1 : vwozg 33 moduleB get data from Thread - 1 : vwozg 33 moduleB get data from Thread - 0 : mkwrt 35 moduleB get data from Thread - 2 : hubdn 30
创建 3 个线程,每个线程要保存一个 Passenger 对象,并且通过 ModuleA 、ModuleB 来访问每个线程对应保存的 Passenger 对象。
上面我们讨论的是多线程之间如何访问自己的变量。那么多线程之间共享变量时如何的呢,看下的例子,线程 1 对共享变量进行减一操作,线程 2 对共享变量进行加 2 操作。
View Code
- public class MutilThreadShareVariable {
- static volatile int count = 100;
- public static void main(String[] args) throws Exception {
- final ShareDataDec sdDec = new ShareDataDec();
- final ShareDataInc sdInc = new ShareDataInc();
- //线程1
- new Thread(new Runnable() {@Override public void run() {
- for (int i = 0; i < 5; i++) {
- sdDec.dec();
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }).start();
- //线程2
- new Thread(new Runnable() {@Override public void run() {
- for (int i = 0; i < 5; i++) {
- sdInc.inc();
- try {
- Thread.sleep(1000);
- } catch(InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }).start();;
- }
- static class ShareDataDec {
- public synchronized void dec() {
- count--;
- System.out.println("Thread " + Thread.currentThread().getName() + " dec 1 from count,count remain " + count);
- }
- }
- static class ShareDataInc {
- public synchronized void inc() {
- count = count + 2;
- System.out.println("Thread " + Thread.currentThread().getName() + " inc 2 from count,count remain " + count);
- }
- }
- }
运行结果
View Code
- Thread Thread - 0 dec 1 from count,
- count remain 99 Thread Thread - 1 inc 2 from count,
- count remain 101 Thread Thread - 0 dec 1 from count,
- count remain 100 Thread Thread - 1 inc 2 from count,
- count remain 102 Thread Thread - 0 dec 1 from count,
- count remain 101 Thread Thread - 1 inc 2 from count,
- count remain 103 Thread Thread - 0 dec 1 from count,
- count remain 102 Thread Thread - 1 inc 2 from count,
- count remain 104 Thread Thread - 0 dec 1 from count,
- count remain 103 Thread Thread - 1 inc 2 from count,
- count remain 105
线程共享变量,只要对要对共享变量进行修改的代码进行同步即可。
来源: http://www.cnblogs.com/hjwublog/p/6133278.html