多线程共享数据的方式:
1,如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,卖票系统就可以这么做。网上搜出来的东西都直接这样解说,包括传智播客中张孝祥也是这么讲的。但是我却迷茫了好久,为啥这个Runnable实现的多线程共享数据,在没有采取任何措施的情况下,没有出现执行混乱。当然网上没有找到我想要的答案。后来还是自己想明白了,虽然Runnable能实现数据对象共享,但是它并不能保证程序执行不混乱(自己代码测试)。大家可以试一下,可以在买票系统的方法中多加几行代码就会发现执行混乱问题。下面代码所示:
public class ThreadSynchronized {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable,"窗口一").start();
new Thread(myRunnable,"窗口二").start();
new Thread(myRunnable,"窗口三").start();
}
}
class MyRunnable implements Runnable{
private int piao = 10;
@Override
public void run() {
while(piao > 0){
System.out.println("开始卖票");
piao--;
System.out.println(Thread.currentThread().getName()+"卖出票:"+piao);
}
}
}
结论:Runnable实现多线程数据共享,是不能保证线程互斥正常执行的,依然要采取措施才能保证,程序的正常执行顺序。
2,如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,例如,设计4个线程。其中两个线程每次对j增加1,另外两个线程对j每次减1,银行存取款有两种方法来解决此类问题:将共享数据封装成另外一个对象,然后将这个对象逐一传递给各个Runnable对象,每个线程对共享数据的操作方法也分配到那个对象身上完成,这样容易实现针对数据进行各个操作的互斥和通信;将Runnable对象作为一个类的内部类,共享数据作为这个类的成员变量,每个线程对共享数据的操作方法也封装在外部类,以便实现对数据的各个操作的同步和互斥,作为内部类的各个Runnable对象调用外部类的这些方法。
public class MultiThreadShareData {
public static void main(String[] args) {
ShareData task = new ShareData(); //公共数据和任务放在task中
for(int i = 0; i < 2; i ++) { //开启两个线程增加data
new Thread(new Runnable() {
@Override
public void run() {
task.increment();
}
}).start();
}
for(int i = 0; i < 2; i ++) { //开启两个线程减少data
new Thread(new Runnable() {
@Override
public void run() {
task.decrement();
}
}).start();
}
}
}
class ShareData /*implements Runnable*/ {
private int data = 0;
public synchronized void increment() { //增加data
System.out.println(Thread.currentThread().getName() + ": before : " + data);
data++;
System.out.println(Thread.currentThread().getName() + ": after : " + data);
}
public synchronized void decrement() { //减少data
System.out.println(Thread.currentThread().getName() + ": before : " + data);
data--;
System.out.println(Thread.currentThread().getName() + ": after : " + data);
}
}
就如上面那个题目所描述的,两个线程执行data增,两个线程执行data减。针对这种情况,我们要实现两个Runnable了,因为很明显有两个不同的任务了,一个任务执行data增,另一个任务执行data减。为了便于维护,可以将两个任务方法放到一个类中,然后将data也放在这个类中,然后传到不同的Runnable中,即可完成数据的共享。