Java如何实现多个线程之间共享数据
目录
- 实现多个线程之间共享数据
- 一、 如果每个线程执行的代码相同
- 二、 如果每个线程执行的代码不同
- 多线程之间共享数据的方式探讨
- 方式一:代码一致
- 方式二:代码不一致
实现多个线程之间共享数据
一、 如果每个线程执行的代码相同
可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如:卖票系统
class Ticket implements Runnable{ private int tick = 20; Object obj = new Object(); public void run(){ while(true){ synchronized(obj){ if(tick>0){ //只能try,因为run是复写了Runnable接口的run,接口的run没有抛 try{Thread.sleep(100);}catch(Exception e){} //使用sleep不然执行每个线程都会占用完毕 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } } } } } class TicketDemo { public static void main(String[] args) { //只建立了一个Ticket对象,内存中只有一个tick成员变量,所以是共享数据 Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); } }
输出结果
Thread-0....sale : 20
Thread-1....sale : 19
.......
Thread-3....sale : 2
Thread-3....sale : 1
二、 如果每个线程执行的代码不同
1、具体实现
将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
思想: 一个类提供数据和操作数据的同步方法,另外定义两个线程通过构造函数接收并操作数据,在主函数中直接创建线程对象,即可完成操作(可以实现两个内部类,不用构造方法传值,使用final定义data局部变量)
例如: 设计4个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1
public class MultyThreadShareMethod1 { public static void main(String[] args){ //将数据封装到一个对象上, ShareData2 data1 = new ShareData2(); //在runnable的构造函数中直接传入去操作 for(int i=0;i<2;i++){ new Thread(new MyRunnable1(data1)).start(); new Thread(new MyRunnable2(data1)).start(); } } } //封装共享数据和操作共享数据方法的类 class ShareData2{ private int j = 10; public synchronized void increment() { j++; System.out.println(Thread.currentThread().getName()+" inc : "+j); } public synchronized void decrement() { j--; System.out.println(Thread.currentThread().getName()+" dec : "+j); } } //增加的线程,需要传入一个共享数据 class MyRunnable1 implements Runnable { private ShareData2 data; public MyRunnable1(ShareData2 data) { this.data = data; } @Override public void run() { for(int i=0;i<10;i++){ data.increment(); } } } //减少的线程,需要传入一个共享数据 class MyRunnable2 implements Runnable { private ShareData2 data; public MyRunnable2(ShareData2 data) { this.data = data; } @Override public void run() { for(int i=0;i<10;i++){ data.decrement(); } } }
输出结果
Thread-0 inc : 11
...
Thread-1 dec : 10
2、 技巧总结
要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥或通信。
极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。
多线程之间共享数据的方式探讨
方式一:代码一致
如果每个线程执行的代码相同,可以用一个 Runnable 对象,这个 Runnable 对象中存放那个共享数据(卖票系统可以这样做)。
public class MultiThreadShareData { public static void main(String[] args) { MyShareData shareData=new MyShareData(); //放入不同的线程中 new Thread(shareData).start(); new Thread(shareData).start(); } } class MyShareData implements Runnable { // 共享的数据 private int count = 100; @Override public void run() { while (count > 0) { synchronized (this) { if (count > 0) { count--; System.out.println(Thread.currentThread().getName() + " 减了1,count还剩:" + count); } } } } }
方式二:代码不一致
如果每个线程执行的代码不同时,就需要不同的 Runnable 对象:
a. 将共享数据封装在一个对象中,然后将这个对象逐一传递给各个 Runnable 对象,每个线程对共享数据操作的方法也分配到这个对象中,这样容易实现针对该数据进行的各个操作的互斥通信。
public class MultiThreadShareData { private int shareData=0; public static void main(String[] args) { ShareData data = new ShareData(); new Thread(new MyRunnable1(data)).start(); new Thread(new MyRunnable2(data)).start(); } } class MyRunnable1 implements Runnable{ private ShareData data; public MyRunnable1(ShareData data){ this.data=data; } @Override public void run() { for(int i=0;i<100;i++){ //对数据进行增加 this.data.increment(); } } } class MyRunnable2 implements Runnable{ private ShareData data; public MyRunnable2(ShareData data){ this.data=data; } @Override public void run() { for(int i=0;i<100;i++){ //对数据进行减少 this.data.decrement(); } } } /** 将共享的数据封装成一个类 */ 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; } }
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; } }
总之,要同步互斥的几段代码最好放在几个独立的方法中,这些方法再放入一个类中,这样比较容易实现它们之间的同步互斥和通信。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。