深入探究一下Java中不同的线程间数据通信方式

2023-05-16 14:05:33 线程 数据通信 探究

1、多线程如何共享数据

多线程数据共享可以分为以下2种情况,线程实现代码相同及线程实现代码不同。

线程实现代码相同

即runnable中的代码一致,这样可以直接在实现中定义成员变量直接共享

public class SharedSameRunnableDemo {
    public static void main(String[] args) {
        Runnable runnable = new MySameRunnable();
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread1.start();
        thread2.start();
    }

    private static class MySameRunnable implements Runnable {
        private int sharedData = 0;

        @Override
        public synchronized void run() {
            for (int i = 0; i < 5; i++) {
                sharedData++;
                System.out.println("Thread: " + Thread.currentThread().getName() + ",
                                   sharedData: " + sharedData);
            }
        }
    }
}

在上面的示例中,我们定义了一个名为 MySameRunnable 的内部类它的共享变量是sharedData,它实现了Runnable 接口,并重写了 run() 方法。在 run() 方法中,我们使用了 synchronized 关键字来保证线程安全。然后在main() 方法中,我们创建了一个 MySameRunnable 实例 runnable,并将其传入两个不同的线程对象中。最后启动这两个线程。由于这两个线程共享同一个 MySameRunnable 实例,因此它们执行的代码是相同的,并且可以访问和修改sharedData 变量。通过这种方式,就可以实现多个线程共享数据,并确保线程安全。

线程实现代码不相同

即runnable中的代码不一致,MyRunnable1 MyRunnable2,利用一个对象SharedData,把runnable中的方法封装到这个对象中去,数据也在这个对象中。如果多个线程实现的代码不同,并且需要共享变量,可以使用一个单独的类来存储这些共享变量,并将它传递给所有的 Runnable 实例。以下是一个简单的示例代码:

public class SharedDifferentRauuableDemo {
    public static void main(String[] args) {
        SharedData sharedData = new SharedData();
        Thread thread1 = new Thread(new MyRunnable1(sharedData));
        Thread thread2 = new Thread(new MyRunnable2(sharedData));
        thread1.start();
        thread2.start();
    }

    private static class SharedData {
        private int data = 0;

        public synchronized void increment() {
            data++;
            System.out.println("IncrementThread: " + data);
        }

        public synchronized void decrement() {
            data--;
            System.out.println("DecrementThread: " + data);
        }
    }

    private static class MyRunnable1 implements Runnable {
        private SharedData sharedData;

        public MyRunnable1(SharedData sharedData) {
            this.sharedData = sharedData;
        }

        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                sharedData.increment();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static class MyRunnable2 implements Runnable {
        private SharedData sharedData;
        public MyRunnable2(SharedData sharedData) {
            this.sharedData = sharedData;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                sharedData.decrement();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在上面的示例中,我们定义了一个 SharedData 类来存储共享变量 data。这个类包含两个同步方法 increment() 和 decrement(),用于增加和减少 data 的值,并输出当前的值。然后我们创建了两个 MyRunnable1 和 MyRunnable2 实例,它们分别传递了相同的 SharedData 对象。在 run() 方法中,它们调用 SharedData 对象的 increment() 和 decrement() 方法来进行数据修改,并使用 Thread.sleep() 方法让线程休眠一段时间。

通过这种方式,就可以实现多个线程共享数据,并确保线程安全。

2、子线程如何继承父线程数据

通过 InteritableThreadLocal实现共享

public class InheritableThreadLocalDemo {

    private static final InheritableThreadLocal<String> inheritableThreadLocal 
    = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        inheritableThreadLocal.set("Hello, World!");

        Thread parentThread = new Thread(() -> {
            System.out.println("Parent Thread: " + inheritableThreadLocal.get());

            inheritableThreadLocal.set("Hello from Parent Thread!");

            Thread childThread = new Thread(() -> {
                System.out.println("Child Thread: " + inheritableThreadLocal.get());
            });

            childThread.start();
            try {
                childThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("Parent Thread: " + inheritableThreadLocal.get());
        });

        parentThread.start();
        try {
            parentThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Main Thread: " + inheritableThreadLocal.get());

        inheritableThreadLocal.remove();
    }
}

在上面的示例中,我们创建了一个 InheritableThreadLocal 对象,并将其设置为“Hello, World!”。然后,我们创建了一个名为“parentThread”的线程,并在其中输出 inheritableThreadLocal 的值。接下来,我们在父线程中将 inheritableThreadLocal 的值设置为“Hello from Parent Thread!”,并创建了一个名为“childThread”的线程,在其中输出 inheritableThreadLocal 的值。

注意:由于 InheritableThreadLocal 是可继承的,所以在子线程中也能够获取到父线程中设置的值。因此,当我们在子线程中输出 inheritableThreadLocal 的值时,我们将看到输出“Hello from Parent Thread!”。

最后,我们分别在父线程、子线程和主线程中输出 inheritableThreadLocal 的值。由于我们在每个线程中都设置了 inheritableThreadLocal 的值,所以每个线程都将输出不同的值。

请注意,在程序的结尾处,我们调用了 inheritableThreadLocal.remove() 方法来清除 InheritableThreadLocal 对象的值,并释放相关的资源。这是一种良好的习惯,可以避免内存泄漏和数据污染等问题。

3、相关问题

1.请简述Java中的多线程通信机制,并解释一下为什么需要多线程通信?

答:Java中的多线程通信机制是通过使用管道和共享变量来实现的。管道可以用来实现多个线程之间的数据传递和同步,共享变量可以用来实现多个线程之间的数据共享和同步。

2.Java 中,在多个线程之间共享数据的方式主要有以下几种:

1)共享变量:可以将需要共享的变量定义为静态变量或公共变量,然后通过同步控制机制(例如 synchronized 关键字、Lock 接口等)保证多线程访问这些变量时的安全性。

2)ThreadLocal:通过 ThreadLocal 类可以在每个线程中创建独立的变量副本,从而避免了对共享变量的竞争。

3)Callable 和 Future 接口:在子线程执行任务后,可以通过 Callable 和 Future 接口返回结果给主线程。主线程可以通过 Future.get() 方法获取子线程的执行结果,从而完成数据共享。

4)BlockingQueue:可以使用 BlockingQueue 来实现数据共享和通信,生产者线程向 BlockingQueue 中添加数据,消费者线程从队列中获取数据。

5)CyclicBarrier 和 CountDownLatch:可以使用 CyclicBarrier 和 CountDownLatch 等同步工具来协调多个线程之间的操作,从而实现数据共享。

到此这篇关于深入探究一下Java中不同的线程间数据通信方式的文章就介绍到这了,更多相关Java线程数据通信方式内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章