【Java多线程与并发库】7.多个线程之间共享数据的方式探讨

多个线程访问共享对象和数据的方式
1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个
Runnable对象中有一个共享数据,例如售票系统就可以这么做这么做。

2.如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有
如下两种方式来实现这些Runnable对象之间的数据共享:
(1)将数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。
每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对
该数据进行的各个操作的互斥和通信。

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

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

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

我们通过完成下边这个面试题来实践该理论:
问题:设计4个线程,其中两个线程每次对j增加1,
另外两个线程对j每次减少1。写出线程。

下面先是我自己的写法:
package cn.edu.hpu.test;

public class ThreadTest8 {

    private static int j = 0;
    
    public static void main(String[] args) {
        R1 r1=new R1();
        R2 r2=new R2();
        new Thread(r1).start();
        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r2).start();
        
    }
    
    static class R1 implements Runnable{

        public synchronized void run() {
            j=j+1;
            System.out.println(Thread.currentThread().getName()
                    +":j增加1,变为:"+j);
        }
        
    }

    static class R2 implements Runnable{

        public synchronized void run() {
            j=j-1;
            System.out.println(Thread.currentThread().getName()
                    +":j减去1,变为:"+j);
        }
        
    }
    
}
效果:


我的思路就是开线程的时候,给run方法加一个线程锁,每一个线程操作j的时候都是
独立的不被打断的。

为了符合一开始的理论,我把j封装到一个Class里面使用:
package cn.edu.hpu.test;

public class ThreadTest8 {

    private static Data data = new Data();
    
    public static void main(String[] args) {
        data.setJ(0);
        R1 r1=new R1(data);
        R2 r2=new R2(data);
        new Thread(r1).start();
        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r2).start();
        
    }
    
    static class R1 implements Runnable{

        private Data data;
        public R1(Data data){
            this.data=data;
        }
        
        public synchronized void run() {
            int j=data.getJ();
            data.setJ(j+1);
            System.out.println(Thread.currentThread().getName()
                    +":j加上1,变为:"+data.getJ());
        }
        
    }

    static class R2 implements Runnable{

        private Data data;
        public R2(Data data){
            this.data=data;
        }
        
        public synchronized void run() {
            int j=data.getJ();
            data.setJ(j-1);
            System.out.println(Thread.currentThread().getName()
                    +":j减去1,变为:"+data.getJ());
        }
        
    }
    
}

class Data{
    
    private int j;

    public int getJ() {
        return j;
    }

    public void setJ(int j) {
        this.j = j;
    }
    
}
这里在创建Runnable的时候,将Data对象传入进去。

下面的代码和上面的代码的区别是,第一段代码的线程是取共用的全局变量中去取数据,
而第二段代码的线程是根据传进来的对象来进行数据的操作,而这个对象是一个共用的全局变量。

但是实际上上面两个方法是有一个严重的问题的,就是数据同步问题。
反复运行上面的代码,会出现类似这种情况:


表面上看每一个线程的Run方法都是加了锁的,但是单单只能保证每一个Runnable在
操作数据的时候不会被打断,但是j的操作权是开放的,别的线程也可以同时修改j的值,
所以这里没有真正实现互斥。

解决办法就是,在要被操作的类中进行加锁,每一个线程在操作这个类的时候,j是否能被加还
是被减的权利是j所在的类而决定的,就是j的提供一个给j加1和给j减1的方法,然后全部加锁,
这样就保证一个线程在改变j的数据的时候,因为方法加锁,所以另外一个线程无法去改变这个值。

最终代码:
package cn.edu.hpu.test;

public class ThreadTest8 {

    private static Data data = new Data();
    
    public static void main(String[] args) {
        data.setJ(0);
        R1 r1=new R1(data);
        R2 r2=new R2(data);
        new Thread(r1).start();
        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r2).start();
        
    }
    
    static class R1 implements Runnable{

        private Data data;
        public R1(Data data){
            this.data=data;
        }
        
        public synchronized void run() {
            data.add();
        }
        
    }

    static class R2 implements Runnable{

        private Data data;
        public R2(Data data){
            this.data=data;
        }
        
        public synchronized void run() {
            data.minus();
        }
        
    }
    
}

class Data{
    
    private int j;
    
    public void setJ(int j){
        this.j=j;
    }

    public synchronized void add(){
        j++;
        System.out.println(Thread.currentThread().getName()
                +":j加上1,变为:"+j);
    }
    
    public synchronized void minus(){
        j--;
        System.out.println(Thread.currentThread().getName()
                +":j减去1,变为:"+j);
    }
}
结果:


可以看到,不论怎么运行,j的值都会平稳的改变。
所以我们按照上面的方式操作就会真正实现多个线程共享数据。
转载请注明出处:http://blog.csdn.net/acmman/article/details/52835908
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光仔December

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值