• java多线程与线程并发二:线程互斥


    本文章内容整理自:张孝祥_Java多线程与并发库高级应用视频教程

    当两条线程访问同一个资源时,可能会出现安全隐患。以打印字符串为例,先看下面的代码:

    //
    public class Test2 {
        public static void main(String[] args) {
            new Test2().init();
        }
        
        public void init(){
            final Outputer c = new Outputer();
            Thread thread1 = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    
                    while (true) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        c.output("aaaaa");
                    }
                    
                }
            });
            thread1.start();
            Thread thread2 = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    
                    while(true){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        c.output("bbbbb");
                    }
                    
                }
            });
            thread2.start();
        }
        
        class Outputer{
            public void output(String name){
    
                    for (int i = 0; i < name.length(); i++) {
                        System.out.print(name.charAt(i));
                    }
                    System.out.println();
    
            }
        }
    }

    在以上代码中,thread1和thread2都调用了output方法,理想的情况下,当thread1调用output方法时,应该一次性打印完“aaaaa”,而当thread2调用output方法,也是一次性打印完“bbbbb”。但在实际的运行中,会出现打印出诸如“aabbbaaa”这种情况,也就是说,thread1正在调用output方法时,thread2插了进来,两个线程同时操作了一个对象中的同一个方法。

    为了避免这种情况,我们要求,当某个线程在调用output方法时,不允许其他线程调用。概括一下,即当某个线程在执行一段代码时,不允许其他线程执行这段代码。我们把这种行为,叫做线程互斥。

    要实现线程互斥,最简单的方法就是加同步锁。

    加同步锁的方式有两种。

    第一种是同步代码块,以上面的代码为例,加同步代码块应该这么加:

        class Outputer{
            public void output(String name){
                synchronized (this) {
                    for (int i = 0; i < name.length(); i++) {
                        System.out.print(name.charAt(i));
                    }
                    System.out.println();
                }
            }
        }

    在上面的代码中,“synchronized (this)”中的“this”被称为锁栓(也有别的叫法,在这里姑且先这么叫吧)。多个需要互斥的线程必须使用同一个对象当锁栓。在这段代码中,“this”指Outputer对象,而两条线程使用的是同一个Outputer对象,所以可以达到互斥效果。

    第二种方式是声明同步方法,写法如下:

            public synchronized void output2(String name){
                System.out.println(this.getClass());
                for (int i = 0; i < name.length(); i++) {
                    System.out.print(name.charAt(i));
                }
                System.out.println();
            }

    现在有一个问题,加入thread1调用output1,thread2调用output2,可以实现线程互斥吗?

    答案是可以的。

    同步方法使用的锁栓就是“this”。当同步代码块也使用“this”当锁栓时,这两个地方的代码就共用了同一个对象当锁栓,当某个线程进入其中一个时,另一个也被锁住了,不让其他线程进来。

    那么,静态同步方法使用的锁栓是什么呢?

    以下面这段代码为例:

        static class Outputer{
            public static synchronized void output3(String name){
                for (int i = 0; i < name.length(); i++) {
                    System.out.print(name.charAt(i));
                }
                System.out.println();
            }
        }

    它的同步锁是Outputer.class,即类的字节码对象。

    最后说一说线程同步和线程互斥的关系。

    同步是一种更为复杂的互斥,而互斥是一种特殊的同步。

      也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!

      总结:

      互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

      同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥。

  • 相关阅读:
    PHP filter_input_array() 函数
    pt-query-digest 用法
    [SDOI2017]苹果树
    C# json 转 xml 字符串
    C# json 转 xml 字符串
    C# double 好用的扩展
    C# double 好用的扩展
    win10 uwp 使用 Microsoft.Graph 发送邮件
    win10 uwp 使用 Microsoft.Graph 发送邮件
    Sublime Text 安装中文、英文字体
  • 原文地址:https://www.cnblogs.com/bailiyi/p/3619983.html
Copyright © 2020-2023  润新知