首页 > 代码库 > Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)

线程创建与终止

线程创建

Thread类与Runnable接口的关系

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()方法

继承Thread并重写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();

实现Runnable接口并重写run方法

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类提供的一些方法来终止线程。

interrupt()\isInterrupted()\interrupted()方法介绍

stop()方法没有做任何的清除操作就粗暴终止线程,释放该线程所持有的对象锁(下文将介绍),受该对象锁保护的其它对象对其他线程可见,因此具有不安全性。

 

suspend()方法会使目标线程会停下来,但仍然持有在这之前获得的对象锁,对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。

 

终上所述,不建议使用stop()方法和suspend()方法来终止线程,通常我们通过interrupt()方法来终止处于阻塞状态和运行状态的线程

 

需要注意的是,interrupt()方法不会中断一个正在运行的线程,仅仅是将线程的中断标记设为true,当调用了阻塞方法之后,线程会不断监听中断标志,如果为true,则产生一个InterruptedException异常,将InterruptedException放在catch中就能终止线程。

 

isInterrupted()方法可以返回中断标记,常用循环判断条件。

 

interrupted()方法测试当前线程是否已经中断,线程的中断标志由该方法清除。interrupted()除了返回中断标记之外,它还会清除中断标记

 

interrupt()用法

看下面例子

 

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:NEWThread-0 state:RUNNABLEThread-0 is looping,i=1Thread-0 is looping,i=2Thread-0 is looping,i=3Thread-0 is looping,i=4flag: falseflag: trueThread-0 state:TIMED_WAITINGThread-0 catch InterruptedException,state:RUNNABLEfalsejava.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,且发出异常后线程被唤醒,以便线程能从异常中正常退出。

 

线程运行状态图

线程从创建到终止可能会经历各种状态。在java.lang.Thread.State类的源码中,可以看到线程有以下几种状态:NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED。各种状态的转换如下:

 

技术分享 

 

当通过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<name.length(); i++) {                System.out.print(name.charAt(i));            }            System.out.println();        }    }}

 

运行结果

hello,i am thread 1hello,i am thread 2hello,i am hellthread 1o,i am thread 2hello,i am thread 2hello,i am thread 1hello,i am thread 2hello,i am threadhel 2lo,i am thread 1

线程1和线程2同时调用output方法进行输出,从运行结果可以看出,线程之间没有执行完各自的输出任务就被交替了运行了。下面通过对象的锁实现线程1和线程2对output方法的互斥访问。

synchronized修饰方法

使用synchronized 对output方法进行修饰,可以让调用者获得锁。synchronized 修饰方法没有显示声明锁的对象,默认是当前方法所在类的对象this

 

public synchronized void output(String name) {    for(int i=0; i<name.length(); i++) {        System.out.print(name.charAt(i));    }    System.out.println();}  

synchronized修饰代码块

使用synchronized 对output方法中的代码块进行修饰,也可以让调用者获得锁。

 

public void output(String name) {    synchronized(this){        for(int i=0; i<name.length(); i++) {            System.out.print(name.charAt(i));        }        System.out.println();    }} 

使用synchronized之后,线程1和线程2output方法实现了互斥访问。

hello,i am thread 1hello,i am thread 2hello,i am thread 1hello,i am thread 2hello,i am thread 1hello,i am thread 2hello,i am thread 1

synchronized用法

先看下面的例子,我们来总结下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 1hello,i am thread 1hello,i am thread 1hello,i am thread 1hello,i am thread 1hello,i am thread 2hello,i am thread 2hello,i am thread 2hello,i am thread 2hello,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 和对象锁能实现线程互斥,但是不能实现线程通信

wait()\notify()\notifyAll()介绍

线程之间的通信通过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()介绍

yieId()的作用是给线程调度器一个提示,告知线程调度器当前线程愿意让出CPU,但是线程调度器可以忽略这个提示。因此,yieId()的作用仅仅是告知线程调度器当前线程愿意让出CPU给其他线程执行(竟然只是愿意,当前线程可以随时反悔,那其他线程也不一定能得到CPU执行),而且不会让当前线程释放对象锁

 

yieId()能让当前线程由运行状态进入到就绪状态,从而让其它具有相同优先级的等待线程获取执行权。但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权,也有可能当前线程又进入到运行状态继续运行。

 

yieId()只建议在测试环境中使用。

 

wait()和yield()的区别

 

1)wait()是让线程由运行状态进入到等待(阻塞)状态,而yield()是让线程由运行状态进入到就绪状态。

2)wait()是让线程释放它所持有对象的锁,而yield()方法不会释放锁。

 

多线程交替输出及volatile应用

下面的例子是“主线程输出三次接着子线程输出三次”,重复两次。

 

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 loophello,i am main thread,loop:1hello,i am main thread,loop:2hello,i am main thread,loop:3main thread end loop,go to waittingThread-0 thread start loophello,i am chrildren thread,loop:1hello,i am chrildren thread,loop:2hello,i am chrildren thread,loop:3Thread-0 thread end loop,go to waittingmain thread start loophello,i am main thread,loop:1hello,i am main thread,loop:2hello,i am main thread,loop:3Thread-0 thread start loophello,i am chrildren thread,loop:1hello,i am chrildren thread,loop:2hello,i am chrildren thread,loop:3

volatile修饰shouldChildren,线程直接读取shouldChildren变量并且不缓存它,修改了shouldChildren立马让其他线程可见,这就确保线程读取到的变量是一致的。

线程本地变量

线程本地变量

线程本地变量,可能称为线程局部变量更容易理解,即为每一个使用该变量的线程都提供一个变量值的副本,相当于将变量的副本绑定到线程中,每一个线程可以独立地修改自己的变量副本,而不会和其它线程的变量副本冲突。在线程消失之后,线程局部变量的所有副本都会被垃圾回收(下面的源码分析中将提到)

ThreadLocal实现分析

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)方法获取到一个mapmap的类型为ThreadLocalMap

这个map其实就是存储线程变量的对象threadLocalsThreadLocalMap是ThreadLocal中的一个内部类,是一个定制的hashmap以便适用于存储线程本地变量。竟然是定制的hashmap,那么就有Entry tablehashmap的内部实现参考上一篇:Java基础加强之集合篇(模块记忆、精要分析))。而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<ThreadLocal> {        /** 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方法初始化。setInitialValueset方法唯一不同的是调用了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。看下面的例子。

ThreadLocal应用

技术分享
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 =http://www.mamicode.com/ 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 =http://www.mamicode.com/ 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<Passenger> thsd = new ThreadLocal<Passenger>();        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 33Thread Thread-2 has put data:hubdn 30Thread Thread-0 has put data:mkwrt 35moduleA get data from Thread-2:hubdn 30moduleA get data from Thread-0:mkwrt 35moduleA get data from Thread-1:vwozg 33moduleB get data from Thread-1:vwozg 33moduleB get data from Thread-0:mkwrt 35moduleB get data from Thread-2:hubdn 30
View Code

创建3个线程,每个线程要保存一个Passenger 对象,并且通过ModuleA ModuleB来访问每个线程对应保存的Passenger 对象。

多线程之间共享变量

上面我们讨论的是多线程之间如何访问自己的变量。那么多线程之间共享变量时如何的呢,看下的例子,线程1对共享变量进行减一操作,线程2对共享变量进行加2操作。

 

技术分享
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 99Thread Thread-1 inc 2 from count,count remain 101Thread Thread-0 dec 1 from count,count remain 100Thread Thread-1 inc 2 from count,count remain 102Thread Thread-0 dec 1 from count,count remain 101Thread Thread-1 inc 2 from count,count remain 103Thread Thread-0 dec 1 from count,count remain 102Thread Thread-1 inc 2 from count,count remain 104Thread Thread-0 dec 1 from count,count remain 103Thread Thread-1 inc 2 from count,count remain 105
View Code

线程共享变量,只要对要对共享变量进行修改的代码进行同步即可。

 

Java基础加强之多线程篇(线程创建与终止、互斥、通信、本地变量)