[置顶] 多线程同步互斥实例——多个线程共享数据

      • 实例问题

        设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1,写出程序。这是一道java线程面试的面试题,这道题在网上有很多答案。那么,答案是如何来的呢? 简单来分析和总结一下。

     • 思路

      由题目可知道,我们需要创建4个线程,实现两个方法,分别是给变量加1,和对同一个变量减1。这里面,所有的方法,必须要保证变量同步。所以,我们可以使用两个Runnable,但是要操作同一个变量,所以,必须有一个可操作的外部变量,并且,操作要实现互斥,也就是不能两个进程同时操作数据。

      • 实现第一步

      我们先以两个线程为例,实现数据减1。则先定义一个类,放入共享外部变量,类实现Runnable,执行减方法,两个线程共用同一个Runnable对象,则实现数据共享。代码如下:

public class MultiThreadShareData {

public static void main(String[] args) {
	ShareData1 data1 = new ShareData1();
	new Thread(new MyRunnable1(data1)).start();
	new Thread(new MyRunnable2(data1)).start();
	
}

class ShareData1 implements Runnable{
private int j=100;
@Override
public void run() {
	while(true){
		j--;
	}

}
}

      • 完善第二步

      第一步中,并没有实现我们题目中的要求,我们及要求加也要求减,所以 要有两个Runnable对象。两个Runnable对象也要操作同一个外部变量,所以,要有一个可操作的外部对象。代码如下

public class MultiThreadShareData {

	private static ShareData1 data1=new ShareData1();
	public static void main(String[] args) {
		ShareData1 data1 = new ShareData1();
		new Thread(new MyRunnable1(data1)).start();
		new Thread(new MyRunnable2(data1)).start();
		
		
	}

}

class MyRunnable1 implements Runnable{

	private ShareData1 data1;
	public MyRunnable1(ShareData1 data1) {
		this.data1 = data1;
	}
	@Override
	public void run() {
		data1.decrement();
	}
	
}

class MyRunnable2 implements Runnable{

	private ShareData1 data1;
	public MyRunnable2(ShareData1 data1) {
		this.data1 = data1;
	}
	@Override
	public void run() {
		data1.increment();
	}
	
}
class ShareData1{
	private int j=0;
	public synchronized void increment(){
		j++;
	}
	
	public synchronized void decrement(){
		j--;
	}

	
}

      • 补充

      我们还可以吧Run那边了对象作为某一个类的内部类,共享数据作为这个外部类中成员变量,每个线程对共享数据的操作方法也分配给外部类,实现数据对共享数据惊醒的哥哥操作的互斥和通信 。 代码如下:

public class MultiThreadShareData {

private static ShareData1 data1=new ShareData1();
public static void main(String[] args) {
	ShareData1 data2 = new ShareData1();
	new Thread(new MyRunnable1(data2)).start();
	new Thread(new MyRunnable2(data2)).start();
	
	final ShareData1 data1 = new ShareData1();
	new Thread(new Runnable() {
		
		@Override
		public void run() {
			data1.decrement();
			
		}
	}).start();
	new Thread(new Runnable() {
		
		@Override
		public void run() {
			data1.increment();
		}
	}).start();
}

}


class MyRunnable1 implements Runnable{

private ShareData1 data1;
public MyRunnable1(ShareData1 data1) {
	this.data1 = data1;
}
@Override
public void run() {
	data1.decrement();
}

}

class MyRunnable2 implements Runnable{

private ShareData1 data1;
public MyRunnable2(ShareData1 data1) {
	this.data1 = data1;
}
@Override
public void run() {
	data1.increment();
}

}
class ShareData1 implements Runnable{
private int j=0;
public synchronized void increment(){
	j++;
}

public synchronized void decrement(){
	j--;
}

}

     •  最后

      按照这个思路,我们给出我们最终的答案,代码如下

package cn.itcast.heima2;

public class MultiThreadShareData {
	public static void main(String[] args) {
		Factory factory = new Factory();
		T1 t1 = new T1(factory);
		T2 t2 = new T2(factory);
		Thread thread1 = new Thread(t1, "加线程1");
		Thread thread2 = new Thread(t1, "加线程2");
		Thread thread3 = new Thread(t2, "减线程1");
		Thread thread4 = new Thread(t2, "减线程2");
		thread1.start();
		thread2.start();
		thread3.start();
		thread4.start();
	}
}

class T1 implements Runnable {
	Factory factory = null;

	T1(Factory factory) {
		this.factory = factory;
	}

	@Override
	public void run() {
		while (true) {
			factory.add();
			try {
				Thread.sleep((int) Math.random() * 10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class T2 implements Runnable {
	Factory factory = null;

	T2(Factory factory) {
		this.factory = factory;
	}

	@Override
	public void run() {
		while (true) {
			factory.min();
			try {
				Thread.sleep((int) Math.random() * 10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

class Factory {
	int j;

	Factory() {
		j = 0;
	}

	synchronized void add() {
		j++;
		System.out.println(Thread.currentThread().getName()+":"+j);
	}

	synchronized void min() {
		j--;
		System.out.println(Thread.currentThread().getName()+":"+j);
	}
}

      • 总结

      • 多线程访问共享对象和数据的方式

        ○ 如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。
        ○ 如果每个线程执行的代码不同,这是需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:
           § 讲共享数据封装在另外一个对象中,然后敬爱那个这个对象逐一传递给各个Runnable对象,每个线程对共享数据的操作方法也分配到这个对象身上去完成,这样容易实现针对该数据进行的哥哥操作的互斥和通信。
           § 将这些Runnable对象作为一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据惊醒的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法
           § 上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
           § 宗旨,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法在放到同一个类中,这样比较容易实现他们之间的同步互斥和通信。

你可能感兴趣的:([置顶] 多线程同步互斥实例——多个线程共享数据)