Java synchronized多线程互斥技术

在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对象锁,这样可以保证两个方法体的锁对象都是同一个,就可以实现了同步代码块的功能,实现了线程之间互斥

猜你喜欢

转载自blog.csdn.net/u010452388/article/details/80626919