在java中,synchronized是用来控制线程同步的,既为了让一段代码不允许多个线程同时访问,需要排队一个一个执行,就像我们生活中排队在公共电话亭打电话一样,一个人打好电话出来,另外个人才可以进去打电话
问题1:描述了synchronized对象锁的问题
问题2:描述了static静态方法加上synchronized的问题
问题1
代码
不要认为加上synchronized就万事大吉了,看下下面一段代码
public class DemoTest01 {
public static void main(String[] args) {
final OuterPuter puter = new OuterPuter();
//第一个线程
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
puter.outerPut1("ABCD");
}
}
}).start();
//第二个线程
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
puter.outerPut1("我爱编程");
}
}
}).start();
}
}
class OuterPuter {
public void outerPut1(String name) {
// 将名字一个字一个字的打印出来
synchronized (name) {//这段代码被要求每次只能一个线程进行执行
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 换行
System.out.println();
}
}
}
运行结果
从结果可以看出,并不是我们想要的结果,我们要的结果是:每行要么是ABCD,要么是我爱编程
从同步代码块synchronized可以看出,后面的对象name,在两次线程执行的时候,对象不是同一个对象我(只有在锁对象为同一个锁),既不是同一个锁,下面我们进行优化
优化方案1
class OuterPuter {
public void outerPut1(String name) {
// 将名字一个字一个字的打印出来
synchronized (this) {//这里所对象改成this,即可
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 换行
System.out.println();
}
}
将锁对象改成this后,因为我们创建对象是在线程开启前,确保对象的唯一,但是千万不能放到两个线程的run方法里,因为放到run方法里,对象创建了两次,锁对象不是唯一的了
优化方案2
class OuterPuter {
public synchronized void outerPut1(String name) {
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 换行
System.out.println();
}
直接在执行的方法上加上synchronized ,其实他的对象锁也是this,所以也可以实现两个线程互斥
问题2
代码
看下下面一段代码,是否能够实现线程互斥
public class DemoTest01 {
public static void main(String[] args) {
final OuterPuter puter = new OuterPuter();
// 第一个线程
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
puter.outerPut2("ABCD");
}
}
}).start();
// 第二个线程
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
OuterPuter.outerPut3("我爱编程");
}
}
}).start();
}
}
class OuterPuter {
public synchronized void outerPut2(String name) {
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 换行
System.out.println();
}
//静态方法
public static synchronized void outerPut3(String name) {
// 将名字一个字一个字的打印出来
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 换行
System.out.println();
}
}
运行结果:
下面我们分析下原因:
这里有另外个知识点:
因为outerPut3方法前面加了static修饰符,静态方法里是无法使用this的,他的锁对象不是this,而是类的Class对象,所以我们需要对代码进行优化
优化方案:
class OuterPuter {
//非静态方法
public void outerPut2(String name) {
//把锁对象也改成类的Class对象
synchronized (OuterPuter.class) {
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 换行
System.out.println();
}
}
//静态方法
public static synchronized void outerPut3(String name) {
// 将名字一个字一个字的打印出来
for (int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
// 换行
System.out.println();
}
}
将outerPut2下的代码块也用类的Class对象锁,这样可以保证两个方法体的锁对象都是同一个,就可以实现了同步代码块的功能,实现了线程之间互斥