可见性原子性有序性的+线程传参的方式+Java如何实现多个线程之间共享数据+线程间通信+死锁产生

//为了均衡CPU和内存的速度差异,增加了缓存 导致了可见性的问题;
//操作系统增加了进程 线程 分时复用CPU,均衡CPU和io设备的速速差异 导致了原子性问题;
//jvm指令重排序(优化指令排序) 导致了有序性的问题

可见性问题是指 线程A修改共享变量,修改后CPU缓存中的数据没有及时同步到内存,线程B读取了内存中的老数据

原子性问题是指 多线个线程增加数据 有几个线程挂了, 数据就会减少;
有序性问题是指 对象创建需要三步,堆中分配内存-初始化-变量指向内存地址;如果重排序会出现1 3 2;导致没有初始化的数据创建;

//如果每个线程执行的代码相同:可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如:卖票系统
//如果每个线程执行的代码不同: 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

可见性问题实例:

        线程共享:只建立了一个Ticket对象,内存中只有一个tick成员变量,所以是共享数据

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

        可见性问题 导致数据不一致需要加锁 定义一个共享的锁对象

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

class TicketInfo  implements Runnable{

    private int ticketNumber=20;
    @Override
    public void run() {
        while(true){
            if(ticketNumber>0){
                try {
                    //使用sleep不然执行每个线程都会占用完毕
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName()+"....sale : "+ ticketNumber--);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 public static void main(String[] args) {
        int num=10;
        TicketInfo ticket = new TicketInfo();
        //如果每个线程执行的代码相同:可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如:卖票系统
        //只建立了一个Ticket对象,内存中只有一个tick成员变量,所以是共享数据
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);
        Thread t4 = new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        //可见性问题是指 线程A修改共享变量,修改后CPU缓存中的数据没有及时同步到内存,线程B读取了内存中的老数据
    }

线程传参的方式

//线程传参的方式2类4种
//第一类:主动给线程传参
//通过构造函数给Thread进行传递
//通过变量和方法给Runnable传递数据
//第二类 线程主动获取参数
//Thread线程主动获取参数通过回调函数获取
//线程Exchanger工具类实现线程间的数据交换

主动给线程传参;

class TicketInfo  implements Runnable{
    private  int ticketNumber=0;
    //通过构造函数进行传参
    TicketInfo(int ticketNumber){
        this.ticketNumber=ticketNumber;
    }
    /**
     * 第二种通过变量传参
     */
    private String parameter ;
    public void setParameter(String parameter) {
        this.parameter  = parameter ;
    }
    @Override
    public void run() {
        try{
            while(true){
                if(ticketNumber>0){
                    try {
                        //使用sleep不然执行每个线程都会占用完毕
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName()+".."+parameter+"..sale : "+ ticketNumber--);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }

    }
}
public static void main(String[] args) {
        TicketInfo ticket = new TicketInfo(20);
        //如果每个线程执行的代码相同:可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如:卖票系统
        //只建立了一个Ticket对象,内存中只有一个tick成员变量,所以是共享数据
        ticket.setParameter("ticket");
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);
        Thread t4 = new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        //可见性问题是指 线程A修改共享变量,修改后CPU缓存中的数据没有及时同步到内存,线程B读取了内存中的老数据
    }

使用回调函数

public class TestInfox extends  Thread{

    private Work work;
    TestInfox(Work work){
        this.work = work;
    }

    @Override
    public void run() {
        Random random = new Random();
        Data data = new Data();
        int n1 = 2;//random.nextInt(1000);
        int n2 = 3;//random.nextInt(2000);
        int n3 = 4;//random.nextInt(3000);
        Integer[] numbers = {n1,n2,n3};
        //1.把数据源
        work.process(data, numbers); // 使用回调函数
        //线程使用回调函数取数据
        //System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+" + String.valueOf(n3) + "=" + data.value);
    }
    public static void main(String[] args) {
        Thread thread = new TestInfox(new Work());
        thread.start();
    }
}
class Data{
    int value=0;

    @Override
    public String toString() {
        return "Data{" +
                "value=" + value +
                '}';
    }
}
class Work{
    public  void process(Data data,Integer[] numbers){
        System.out.println("data->"+data+" --array--"+ Arrays.toString(numbers));
        for(int n:numbers){
            data.value+=n;
        }
    }
}

Java如何实现多个线程之间共享数据

实现多个线程之间共享数据

一、 如果每个线程执行的代码相同

可以使用同一个Runnable对象,这个Runnable对象中有能共享数据,例如 买票系统
class TicketInfo  implements Runnable{
    private  int ticketNumber=0;
    //通过构造函数进行传参
    TicketInfo(int ticketNumber){
        this.ticketNumber=ticketNumber;
    }
    /**
     * 第二种通过变量传参
     */
    private String parameter ;
    public void setParameter(String parameter) {
        this.parameter  = parameter ;
    }
    Object lock = new Object(); // 定义一个共享的锁对象
    @Override
    public void run() {
        try{
            while(true){
                synchronized (lock){
                    if(ticketNumber>0){
                        try {
                            //使用sleep不然执行每个线程都会占用完毕
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName()+".."+parameter+"..sale : "+ ticketNumber--);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }

    }
}

2.如果每个线程执行的代码不同

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

思想: 一个类提供数据和操作数据的同步方法,另外定义两个线程通过构造函数接收并操作数据,在主函数中直接创建线程对象,即可完成操作(可以实现两个内部类,不用构造方法传值,使用final定义data局部变量

例如: 设计4个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1

//取款线程 需要传入一个共享数据
class MyRunnable2 implements Runnable{
    private ShareDate data;
    private int num;
    MyRunnable2(ShareDate data,int num){
        this.data = data;
        this.num = num;
    }
    @Override
    public void run() {
        if(num>0){
            for(int i=0;i<num;i++){
                data.decrement();
            }
        }
    }
}
//存款的线程 需要传入一个数据数据
class MyRunnabe1 implements  Runnable{
    private ShareDate data;
    private int num;
    MyRunnabe1(ShareDate data,int num){
        this.num=num;
        this.data=data;
    }
    @Override
    public void run() {
        if(num>0){
            for(int i=0;i<num;i++){
                data.increment();
            }
        }

    }
}
//封装共享数据和操作共享数据方法的类
class ShareDate{
    private int j=10;
    public synchronized void increment(){
        j++;
        System.out.println(Thread.currentThread().getName()+" add :"+j);
    }
    public synchronized void decrement(){
        j--;
        System.out.println(Thread.currentThread().getName()+" del :"+j);
    }

}

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

        //思想: 一个类提供数据和操作数据的同步方法,另外定义两个线程通过构造函数接收并操作数据,
        // 在主函数中直接创建线程对象,即可完成操作
        // (可以实现两个内部类,不用构造方法传值,使用final定义data局部变量)

        //例如: 设计4个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1
        //将数据封装到一个对象,内存中只有一个data成员变量,是共享数据
        ShareDate data=new ShareDate();
        for (int i = 0; i <2;i++ ) {
            System.out.println("内存中只有一个data成员变量");
            new Thread(new MyRunnabe1(data,1)).start();//12
            new Thread(new MyRunnable2(data,2)).start();//12-4=8
        }
        //要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥或通信。

    }

多线程之间共享数据的方式探讨

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

方式一:代码一致

如果每个线程执行的代码相同,可以用一个Runnable对象,这个Runnable对象中存放能共享数据(卖票系统)

方式二:代码不一致

如果每个线程执行的代码不同时,就需要不同的 Runnable 对象:
a.将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据操作的方法也分配到这个对象中,这样容易实现针对改数据进行的各个操作的互斥通信

实例代码为: 设计4个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1

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

public class MultiThreadShareData { 
    private int shareData=0; 
    public static void main(String[] args) {
        MultiThreadShareData m=new MultiThreadShareData();
        //初始化Runnable对象
        MyRunnable1 myRunnable1 = m.new MyRunnable1();
        MyRunnable2 myRunnable2=m.new MyRunnable2(); 
        //开启线程
        new Thread(myRunnable1).start(); 
        new Thread(myRunnable2).start();
    }
  
    private synchronized void increment(){
        this.shareData++;
        System.out.println(Thread.currentThread().getName()+":shareData增加了1后shareData="+shareData);
    }
  
    private synchronized void decrement() {
        this.shareData--;
        System.out.println(Thread.currentThread().getName()+":shareData减少了1后shareData="+shareData);
    }
  
    /**
     * 作为内部类的Runnable对象
     */
    class MyRunnable1 implements Runnable{ 
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                increment();
            }
        }
    }
  
    class MyRunnable2 implements Runnable{ 
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                decrement();
            }
        }
    }
}

c.. 以上两种方法的组合:将共享数据封装到一个对象中,每个线程对共享数据的操作方法也分配到对象中,对象作为外部类的成员变量或方法中的局部变量,每个线程的 Runnable 作为成员内部类或局部内部类。

public class MultiThreadShareData {
   public static void main(String[] args) { 
       ShareData data = new ShareData(); 
       new Thread(()->{
           for(int i=0;i<100;i++){
               data.increment();
           }
       }).start();
  
       new Thread(()->{
           for (int j=0;j<100;j++) {
               data.decrement();
           }
       }).start();
   } 
}
  
/**
封装共享数据的对象
*/
class ShareData{
   //共享数据
   private int j=0;
  
   /**
    对共享数据进行操作的方法
   */
   public synchronized void increment(){
       this.j++;
       System.out.println(Thread.currentThread().getName()+":j增加了1后j="+j);
   }
  
   public synchronized void decrement() {
       this.j--;
       System.out.println(Thread.currentThread().getName()+":j减少了1后j="+j);
   }
  
   public int getJ() {
       return j;
   }
}

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

线程通信(同步)

wait/notify 实例

public class ThreadExecutionOrder {
    private static int sharedCount = 0;
    public static void main(String[] args) {
        Object lock = new Object(); // 定义一个共享的锁对象
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(lock) { // 通过共享的锁对象来实现同步
                    while(sharedCount != 0) { // 如果前一个线程还未执行完,则等待
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Thread 1");
                    sharedCount = 1; // 修改共享变量的值,表示当前线程已经完成任务
                    lock.notifyAll(); // 唤醒其他等待线程
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(lock) {
                    while(sharedCount != 1) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Thread 2");
                    sharedCount = 2;
                    lock.notifyAll();
                }
            }
        });

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(lock) {
                    while(sharedCount != 2) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Thread 3");
                    sharedCount = 3;
                    lock.notifyAll();
                }
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }
}

死锁产生问题

public class LockLock {
    //创建资源
    private static Object resourceA =new Object();//定义一个共享锁对象
    private static Object resourceB =new Object();//定义一个共享锁对象

    public static void main(String[] args) throws InterruptedException {
        Thread threadA=new Thread(()->{
            //线程A使用synchronized(resourceA)方法获取到了resourceA的监视器锁;
            synchronized (resourceA){
                System.out.println(Thread.currentThread().getName() +" get ResourceA");
                try {
                    Thread.sleep(1000);//调用sleep函数休眠1s 休眠1s是为了保证线程A在获取resourceB
                    //对应的锁前让线程B抢占到CPU 获取到资源resourceB上的锁;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() +" waiting get resourceB");
                synchronized (resourceB){
                    System.out.println(Thread.currentThread().getName()+" get resourceB");
                }
            }
        });
        //产生死锁
        /*Thread threadB =new Thread(()->{
            //获取resourceB 共享资源的监视器锁
            synchronized (resourceB){
                System.out.println(Thread.currentThread().getName()+" get ResourceB");
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" waiting get resourceA");
                //获取resourceA共享资源的监视器锁
                synchronized (resourceA){
                    System.out.println(Thread.currentThread().getName()+"get resourceA");
                }
            }
        });*/
        //改变执行顺序 避免死锁
        Thread threadB =new Thread(()->{
            //获取resourceB 共享资源的监视器锁
            synchronized (resourceA){
                System.out.println(Thread.currentThread().getName()+" get ResourceB");
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" waiting get resourceA");
                //获取resourceA共享资源的监视器锁
                synchronized (resourceB){
                    System.out.println(Thread.currentThread().getName()+"get resourceA");
                }
            }
        });


        /*
        首先 resourceA和 resourceB 都是互斥资源,当线程A调用 synchronized(resource A)
        方法获取到 resourceA 监视器锁并释放前, 线程B再调 synchronized(resourceA
        法尝试获取该资源会被阻塞,只有线程A主动释放该锁,线程B能获得 这满足了资源互斥条件
         */
        threadA.start();
        //threadA.join();
        threadB.start();
        //要想避免死锁,只需要破坏掉至少一个构造死锁的必要条件即可;
        //学过操作系统的朋友知道 目前只有请求并持有和环路等待条件是可以被破会的
        //1.造成死锁的原因其实和申请资源的顺序有很大关系; 使用资源申请的有序性原则就可以避免死锁,
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值