传统线程机制之线程间共享数据的方式

多个线程访问共享对象和数据的方式

接下来我们探讨多个线程之间共享数据的方式:

现在有A线程,B线程,C线程,要访问一个共同的数据,我们程序代码该怎么写?举个例子

首先,我们来写一段卖票的代码,刚开始一共有100张票,然后,有两个窗口在卖这100张票,代码如下:

public class MultiThreadShareData2 {

    public static void main(String[] args) {

        ShareData1 data1 = new ShareData1();

        new Thread(data1).start();

        new Thread(data1).start();

    }

}

class ShareData1 implements Runnable {

        private int count = 100;// 这个数据是所有运行这个runnable对象的线程共享的

        @Override

        public void run() {

            while (true) {

                count--;

            }

        }

    }

//这部分代码存在线程安全问题,因为count--不是原子性操作,减运算和赋值运算,所以在此过程中可能被其它线程打断,造成数据污染。

//我们可以使用原子操作类来声明count变量,解决线程安全问题。

以上代码思路:

多个线程之间共享数据的方式一:把数据封装在Runnable对象(封装数据的类实现Runnable接口)中,要共享数据的线程使用同一个Runnable对象,数据就共享了。也就是:如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable中封装了那个共享数据,把Runnable传给哪个线程,哪个线程就开始操作这个Runnable里面的数据了。这个Runnable同时传给100个线程,那么这100个线程就同时操作这一个Runnable对象。

设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。

接下来,如果要共享数据的这些线程,它们要运行的代码不同,该怎么共享数据呢?比如说我一个线程要做加法,一个线程要做减法,这样就需要两个Runnable对象,需要两个run方法,一个run方法负责减,一个run方法负责加。数据对象只能有一个,因为要共享,但是Runnable对象要有两个,因为线程要做两件不同的事。这应该怎么实现呢?

以下是初步实现的代码:

public class MultiThreadShareData2 {

    public static void main(String[] args) {

        final ShareData1 data1 = new ShareData1();

        new Thread(new Runnable() {

           

            @Override

            public void run() {

                data1.increment();

               

            }

        }).start();

        new Thread(new Runnable() {

           

            @Override

            public void run() {

                data1.decrement();

               

            }

        }).start();

    }

    static class ShareData1{

        private int j = 0;

        public synchronized void increment(){//有多个线程操作,需要同步

            j++;

        }

        Public synchronized  void decrement(){

            j--;

        }

    }

}

这样,就要有两个Runnable对象,因为它们的代码不一样,所以Runnable对象就要有多个。第一个的run方法运行减的逻辑,第二个的run方法运行加的逻辑。由于它们操作的是同一个ShareData1对象,那个共享数据j装在同一个ShareData1对象里面,这样就及可以运行不同的代码逻辑,又可以共享数据。这是一种方案。

这里,我们总结一下多线程访问共享对象和数据的几种方案:

l  如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。

l  如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:

Ø  将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

public class MultiThreadShareData2 {

    public static void main(String[] args) {

        ShareData1 data2 = new ShareData1();

        new Thread(new MyRunnable1(data2)).start();

        new Thread(new MyRunnable2(data2)).start();

    }

}

    class MyRunnable1 implements Runnable {

        private ShareData1 data1;

        public MyRunnable1(ShareData1 data1) {

            this.data1 = data1;

        }

        public void run() {

            data1.decrement();

        }

    }

    class MyRunnable2 implements Runnable {

        private ShareData1 data1;

        public MyRunnable2(ShareData1 data1) {

            this.data1 = data1;

        }

        public void run() {

            data1.increment();

        }

    }

    class ShareData1 {

        private int j = 0;

        public synchronized void increment() {

            j++;

        }

        public synchronized void decrement() {

            j--;

        }

    }

把共享数据放到一个对象里面,对数据的操作方法,也放到这个对象里面,然后有两个runnable都去操作同一个对象,就ok了。

Ø  将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。

public class MultiThreadShareData {

//    private static ShareData1 data1 = new ShareData1();

    public static void main(String[] args) {

        final ShareData1 data1 = new ShareData1();

        new Thread(new Runnable() {

            @Override

            public void run() {

                data1.decrement();

            }

        }).start();

        new Thread(new Runnable() {

            @Override

            public void run() {

                data1.increment();

            }

        }).start();

    }

}

class ShareData1{

    private int j = 0;

    public synchronized void increment() {

        j++;

    }

    public synchronized void decrement() {

        j--;

    }

}

一个外部类里面有两个内部类,这两个内部类如何共享数据,都操作外部类的同一个成员,是不是可以,因为A内部也可以访问外部类的成员,B内部类也可以访问外部类的成员,它们共用了同一个外部类,所以,两个Runnable对象要共享同一份数据,可以把两个runnable作为一个类的内部类,把他们共享的数据作为那个外部类的成员变量。

Ø  上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。

Ø  总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。

l  极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。

那么,现在我们再回到之前的那道代码题:设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。

代码实现如下:

//方案一,内部类共享成员变量

public class ThreadTest1 {

    private int j;

    public static void main(String args[]) {

        ThreadTest1 tt = new ThreadTest1();

        Inc inc = tt.new Inc();

        Dec dec = tt.new Dec();

        for (int i = 0; i < 2; i++) {

            Thread t = new Thread(inc);

            t.start();

            t = new Thread(dec);

            t.start();

        }

    }

    private synchronized void inc() {

        j++;

        System.out.println(Thread.currentThread().getName() + "-inc:" + j);

    }

    private synchronized void dec() {

        j--;

        System.out.println(Thread.currentThread().getName() + "-dec:" + j);

    }

    class Inc implements Runnable {

        public void run() {

            for (int i = 0; i < 100; i++) {

                inc();

            }

        }

    }

    class Dec implements Runnable {

        public void run() {

            for (int i = 0; i < 100; i++) {

                dec();

            }

        }

    }

}

//共享数据封装到一个对象,然后内部类共享这个对象

class A {

    public static void main(String[] args) {

        new A().call();

    }

   

    JManager j = new JManager();

   

    public void call() {

        for (int i = 0; i < 2; i++) {

            new Thread(new Runnable() {

                public void run() {

                    while (true) {

                        j.accumulate();

                    }

                }

            }).start();

            new Thread(new Runnable() {

                public void run() {

                    while (true) {

                        j.subtract();

                    }

                }

            }).start();

        }

    }

}

class JManager {

    private int j = 0;

    public synchronized void subtract() {

        j--;

    }

    public synchronized void accumulate() {

        j++;

    }

}

猜你喜欢

转载自my.oschina.net/u/3512041/blog/1822016