欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

个人对于Java线程+synchronized+单例模式的一些思考和尝试

程序员文章站 2022-03-10 08:43:54
...

这段时间在项目中遇到了线程线程锁以及单例模式的问题,就自己学习了一下,并在此把自己理解和学习的东西总结一下。

做了以下尝试

首先,这里只是使用synchronized修饰了代码段,听说还可以修饰变量和类,这个以后尝试了再更新他们的使用和区别

其次,我也不是特别确定我这里使用的synchronized是不是叫做真正的线程锁。个人理解为,他是限制一段代码或者一些资源,在同一时间只能被一个计算使用。

1、单例的实体类Apple,和他的操作类AppleOperater 使用synchronized关键字

//此处创建一个单例的Apple
public class Apple {
	private Apple() {
		count = 0;
	}
	public int count;
	private static Apple singleApple = null;
	
	public static Apple getInstance() {
		if(singleApple == null) {
			singleApple = new Apple();
		}
		return singleApple;
	}
	
	public int getCount() {
		return this.count;
	}
	
	public void addOne() {
		this.count++;
	}
	
}
public class AppleOperater implements Runnable{
	private Apple apple = Apple.getInstance();
	
	
	public void run() {
		synchronized (apple) {
			
			for(int i=0;i<10;i++) {
				apple.addOne();
				System.out.println(Thread.currentThread().getName() + ":" + apple.getCount());
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

然后再main函数中测试,首先让两个线程跑一个AppleOperater。

public class Demo {
	public static void main(String[] args) {
		AppleOperater appleOperater1= new AppleOperater();
		Thread thread1 = new Thread(appleOperater1,"apple1");
		Thread thread2 = new Thread(appleOperater1,"apple2");
		thread1.start();
		thread2.start();
	}
}

结果如下图。可见是两个线程同时对一个apple进行操作。个人理解为:

可以想象为两个人向一个篮子里放苹果的操作,但是篮子同时只能由一个人持有,所以是apple1先放,apple1把活干完了,appl2在干。

个人对于Java线程+synchronized+单例模式的一些思考和尝试

而后,我又尝试了用两个线程分别跑两个AppleOperater,也就是将main函数改为下面这样

public class Demo {
	public static void main(String[] args) {
		AppleOperater appleOperater1= new AppleOperater();
		AppleOperater appleOperater2= new AppleOperater();
		Thread thread1 = new Thread(appleOperater1,"apple1");
		Thread thread2 = new Thread(appleOperater2,"apple2");
		thread1.start();
		thread2.start();
	
	}
}

得到的结果如下图所示,可见效果相同。

个人对于Java线程+synchronized+单例模式的一些思考和尝试

2、非单例的Peach类 在PeachOperater中使用synchronized关键字

Peach

public class Peach {
	public int count =0;
	public void addOne() {
		this.count++;
	}
	public int getCount() {
		return this.count;
	}
}
PeachOperater代码如下
public class PeachOperater implements Runnable{
	private Peach peach = new Peach();
	
	public void run() {
		synchronized(peach) {
			for(int i=0;i<10;i++) {
				peach.addOne();
				System.out.println(Thread.currentThread().getName() + ":" + peach.getCount());
			}
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public int getPeachCount() {
		return peach.getCount();
	}
	
}

首先两个线程跑一个PeachOperater,

public class Demo {
	public static void main(String[] args) {
		PeachOperater peachOperater1 = new PeachOperater();
		PeachOperater peachOperater2 = new PeachOperater();
		Thread thread1 = new Thread(peachOperater1,"peach1");
		Thread thread2 = new Thread(peachOperater1,"peach2");
		thread1.start();
		thread2.start();
		try {
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("线程1最终的总数为: " + peachOperater1.getPeachCount());
		System.out.println("线程2最终的总数为: " + peachOperater1.getPeachCount());
	}
}

结果如下,可见,线程1先使用被synchronized修饰的代码,然后线程2在使用。

个人对于Java线程+synchronized+单例模式的一些思考和尝试

然后在尝试两个线程分别跑两个PeachOperater,

public class Demo {
	public static void main(String[] args) {
		PeachOperater peachOperater1 = new PeachOperater();
		PeachOperater peachOperater2 = new PeachOperater();
		Thread thread1 = new Thread(peachOperater1,"peach1");
		Thread thread2 = new Thread(peachOperater2,"peach2");
		thread1.start();
		thread2.start();
		
		try {
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("线程1最终的总数为: " + peachOperater1.getPeachCount());
		System.out.println("线程2最终的总数为: " + peachOperater2.getPeachCount());
	}
}

结果如下,可见其相互独立,并且计算结果无误,因为这里只是用synchronized修饰了代码段,并没有影响两个线程之间的数据的独立性,就像这个代码段是个工具,两个线程每次只能一个人使用,俩人随机(还不确定这里的分配机制)的换着用。

个人对于Java线程+synchronized+单例模式的一些思考和尝试

3、此处再尝试一下,使用单例模式,但是不使用synchronized关键字

AppleOperater代码如下

public class AppleOperater implements Runnable{
	private Apple apple = Apple.getInstance();
	
	
	public void run() {
		//synchronized (apple) {
			
			for(int i=0;i<10;i++) {
				apple.addOne();
				System.out.println(Thread.currentThread().getName() + ":" + apple.getCount());
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			//}
		}
	}
	
	public int getAppleCount() {
		return this.apple.getCount();
	}
	
}

首先,当两个线程跑一个AppleOperater的时候。main函数更改为

public class Demo {
	public static void main(String[] args) {
		AppleOperater appleOperater1= new AppleOperater();
		AppleOperater appleOperater2= new AppleOperater();
		Thread thread1 = new Thread(appleOperater1,"peach1");
		Thread thread2 = new Thread(appleOperater1,"peach2");
		thread1.start();
		thread2.start();
		try {
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("最终的总数为: " + appleOperater1.getAppleCount());
	}
}

其结果如下图所示,可见这样是有问题的,中间的过程不对,输出结果也不对。(具体准确的原因尚不清楚),并且多次尝试后结果不稳定,综述为17、18、19、20的情况都出现过。

个人对于Java线程+synchronized+单例模式的一些思考和尝试

然后,进行两个线程跑两个AppleOperater,main函数更改为

public class Demo {
	public static void main(String[] args) {
		AppleOperater appleOperater1= new AppleOperater();
		AppleOperater appleOperater2= new AppleOperater();
		//PeachOperater peachOperater1 = new PeachOperater();
		//PeachOperater peachOperater2 = new PeachOperater();
		Thread thread1 = new Thread(appleOperater1,"peach1");
		Thread thread2 = new Thread(appleOperater2,"peach2");
		thread1.start();
		thread2.start();
		
		try {
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("线程1最终的总数为: " + appleOperater1.getAppleCount());
		System.out.println("线程2最终的总数为: " + appleOperater2.getAppleCount());
	}
}

输出结果如图,同样,中间过程不对,最终结果也不对,且最终结果不稳定,17、18、19、20都会出现。

个人对于Java线程+synchronized+单例模式的一些思考和尝试

4、非单例模式,然后也不使用synchronized关键字

public class PeachOperater implements Runnable{
	private Peach peach = new Peach();
	
	public void run() {
		for(int i=0;i<10;i++) {
			peach.addOne();
			System.out.println(Thread.currentThread().getName() + ":" + peach.getCount());
		}
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

在main函数中进行测试,首先用两个线程跑一个PeachOperater。

public class Demo {
	public static void main(String[] args) {
		PeachOperater peachOperater1 = new PeachOperater();
		PeachOperater peachOperater2 = new PeachOperater();
		Thread thread1 = new Thread(peachOperater1,"peach1");
		Thread thread2 = new Thread(peachOperater1,"peach2");
		thread1.start();
		thread2.start();
		try {//这个try/catch段的作用是等待线程执行完毕之后在执行后面的操作
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("最终的总数为: " + peachOperater1.getPeachCount());
	}
}

结果如下图,可见,这种情况的中间结果有些混乱,而且有错误,但是最终总数没错(插不上图了,贴的结果),也不是很清楚为什么中间过程不对,但是最终结果正确。

peach2:2
peach1:2
peach2:3
peach1:4
peach2:5
peach1:6
peach2:7
peach1:8
peach2:9
peach1:10
peach2:11
peach1:12
peach2:13
peach2:15
peach1:14
peach2:16
peach1:17
peach2:18
peach1:19
peach1:20
最终的总数为: 20

然后再用两个线程跑两个PeachOperater,

public class Demo {
	public static void main(String[] args) {
		PeachOperater peachOperater1 = new PeachOperater();
		PeachOperater peachOperater2 = new PeachOperater();
		Thread thread1 = new Thread(peachOperater1,"peach1");
		Thread thread2 = new Thread(peachOperater2,"peach2");
		thread1.start();
		thread2.start();
		
		try {
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

结果如下,可见,在这个代码中,线程1和线程2相互独立,其中进行的操作和数据互不影响。

个人对于Java线程+synchronized+单例模式的一些思考和尝试

总结:

这里欠一个总结,等回头总结思考了在写


相关标签: Java