我们设想一个这样的场景,有一个水杯,可以加水和喝水,不能边加水边喝水。喝水和加水就相当于两个线程,我们直接看代码。
package gouzaos;
import java.util.ArrayList;
import java.util.List;
public class Str {
public static void main(String[] args) {
//杯子
Cup cup = new Cup();
//多线程调用这个方法
new Thread(() -> {
cup.set();
}).start();
new Thread(() -> {
cup.get();
}).start();
// Cup cup01 = new Cup();
// Cup cup02 = new Cup();
// 多线程调用这个方法--并行执行
// new Thread(() -> {
// cup01.set();
// }).start();
// new Thread(() -> {
// cup02.get();
// }).start();
// StringBuffer s1 = new StringBuffer();
// StringBuffer s2 = new StringBuffer();
// new Thread(() -> {
// s1.append("a");
// }).start();
// new Thread(() -> {
// s1.append("b");
// }).start();
// new Thread(() -> {
// s2.append("c");
// }).start();
}
}
class Cup {
//创建一个容器
private List list = new ArrayList<>();
public synchronized void set() {
System.out.println("Hello13SynchronizedCpu.set我在向杯子里加水--开始");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello13SynchronizedCpu.set我在向杯子里加水--结束");
}
public synchronized void get() {
System.out.println("Hello13SynchronizedCpu.get我在喝水--开始");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello13SynchronizedCpu.get我在喝水--结束");
}
}
我们看代码的执行结果
为什么这个时候的线程是安全的呢?大家仔细看一看set get方法前的修饰符,可以理解为正是因为有了synchronized,使得当set方法执行的时候会上锁,或者get方法执行的时候会上锁,防止了刚开始加水,就开始了喝水。保证了线程的安全。大家可以尝试一下,假如把synchronized去掉的话,再执行结果就会发现线程是不安全的。
这里大家要记住两句话,静态方法隶属于类,普通方法隶属于对象。
这里的set方法,get方法都是普通方法,隶属于对象,synchronized锁住的可以是一个对象,也可以是一个数据。那么当我们把注释的第二段代码放开的话,也就是cup01,cup02就会发现,线程是不安全的。因为分别new出了两个对象,相当于在堆里面分别指向了两个新的对象。synchronized就失效了。
最后子月补充一点,StringBufer为什么被称为线程安全的呢?其实大家一看源码就明白了,它的源码的方法上都是有synchronized修饰的。相当于本身就封装了synchronized.大家结合着上面的例子来理解StringBuffer。
我们来看代码中第三部分被注释的地方,s1.append("a")和s1.append("b")就是线程安全的。s1和s2指向了不同的对象,二者之间也是互不干扰的。
如果有什么疑惑,欢迎大家评论区留言哦!(*^▽^*)



