前言
- Java 里面进行多线程通信的主要方式就是共享内存的方式。
- 共享内存主要有三个关注点:可见性、有序性、原子性。
- Java内存模型(JVM)解决了可见性和有序性的问题,而锁解决了原子性的问题。
- 在理想情况下,我们希望做到同步和互斥来实现数据在多线程环境下的一致性和安全性。常用的实现多线程数据共享的方式有将数据抽象成一个类,并将对这个数据的操作封装在类的方法中;将Runnable对象作为一个类的内部类,将共享数据作为这个类的成员变量。
方法一
将数据抽象成一个类,并将对这个数据的操作封装在类的方法中。(这种方式只需要在方法上加synchronized关键字即可做到数据的同步。
- 将数据抽象成MyData类,并将数据的操作作为类的方法(方法上加synchronized关键字)。
public class MyData {
private int j = 0;
public synchronized void add(){
j++;
System.out.println("线程"+Thread.currentThread().getName()+"j为:"+j);
}
public synchronized void dec(){
j--;
System.out.println("线程"+Thread.currentThread().getName()+"j为:"+j);
}
public int getData(){
return j;
}
}
- 线程使用该类的对象并调用类的方法对数据进行操作
public class AddRunnable extends Thread{
MyData data;
public AddRunnable(MyData data) {
this.data = data;
}
@Override
public void run() {
data.add();
}
}
public class DecRunnable extends Thread {
MyData data;
public DecRunnable(MyData data) {
this.data = data;
}
@Override
public void run() {
data.dec();
}
}
- 测试
public class Demo {
public static void main(String[] args) {
MyData data = new MyData();
for (int i = 0; i < 2; i++) {
new AddRunnable(data).start();
new DecRunnable(data).start();
}
}
}
运行结果:
线程Thread-0j为:1
线程Thread-1j为:0
线程Thread-2j为:1
线程Thread-3j为:0
- 注意
1)对数据 j 操作的方法需要使用synchronized修饰,以保障在多个并发线程访问对象 j 时执行加锁操作,以便同时只有一个线程有权利访问,可以保障数据的一致性。
2)synchronized修饰的是普通方法,作用范围一个对象,前面的线程需要保障数据操作的原子性和一致性,就必须传入同一个对象。
方法二
将Runnable对象作为一个类的内部类,将共享数据作为这个类的成员变量。
- 共享数据:
public class MyData {
private int j = 0;
public synchronized void add(){
j++;
System.out.println("线程"+Thread.currentThread().getName()+"j为:"+j);
}
public synchronized void dec(){
j--;
System.out.println("线程"+Thread.currentThread().getName()+"j为:"+j);
}
public int getData(){
return j;
}
}
- 将Runnable对象作为一个类的内部类,将共享数据作为这个类的成员变量,每个线程对共享数据的操作方法都被封装在该类的外部类中,以便实现对数据的各个操作的同步和互斥,作为内部类的各个Runnable对象调用外部类的这些方法。
public class Demo02 {
public static void main(String[] args) {
final MyData data = new MyData();
for(int i=0;i<2;i++){
new Thread(new Runnable(){
@Override
public void run() {
data.add();
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
data.dec();
}
}).start();
}
}
}
运行结果:
线程Thread-0j为:1
线程Thread-1j为:0
线程Thread-2j为:1
线程Thread-3j为:0
- 小结
在需要多线程操作数据时直接定义一个内部类的线程,并定义一个MyData类的成员变量,在内部类线程的run方法中直接调用成员变量封装好的数据操作方法,以实现多线程数据的共享。