个人对于Java线程+synchronized+单例模式的一些思考和尝试
这段时间在项目中遇到了线程、线程锁以及单例模式的问题,就自己学习了一下,并在此把自己理解和学习的东西总结一下。
做了以下尝试
首先,这里只是使用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在干。
而后,我又尝试了用两个线程分别跑两个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();
}
}
得到的结果如下图所示,可见效果相同。
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在使用。
然后在尝试两个线程分别跑两个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修饰了代码段,并没有影响两个线程之间的数据的独立性,就像这个代码段是个工具,两个线程每次只能一个人使用,俩人随机(还不确定这里的分配机制)的换着用。
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的情况都出现过。
然后,进行两个线程跑两个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都会出现。
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相互独立,其中进行的操作和数据互不影响。
总结:
这里欠一个总结,等回头总结思考了在写
上一篇: ubuntu中运行php脚本时mkdir(): 权限不够?
下一篇: PHP中使用gRPC客户端