美文网首页
Java多线程及synchronized用法理解

Java多线程及synchronized用法理解

作者: 柳蒿 | 来源:发表于2019-08-17 23:34 被阅读0次

如何写一个多线程Demo

  • 误区一:多线程方法中的public void run()中的代码都是同步方法。
    public void run()方法是多线程方法体。运行时,程序会启动一个线程来运行public void run()中的方法。
    如果在public void run()方法体中写一个简单的循环,然后start两个线程运行这个循环,他们的输出结果应该是交替的。然而实际运行的结果很可能是一边倒的情况,即Thread1先运行完毕,然后Thread2才开始运行,这是因为线程调度出现了一边倒的情况,下一个部分会解释这个问题,并给出解决办法。
   public void Demo1() {
        DeadLock deadLock = new DeadLock();
        new Thread() {
            @Override
            public void run() {
            //    deadLock.cycleFetch(Thread.currentThread().getName(), 10);
                for (int i = 0; i <10; i++) {
                    System.out.println(Thread.currentThread().getName());
                }
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
            //    deadLock.cycleFetch(Thread.currentThread().getName(), 10);
                for (int i = 0; i <10; i++) {
                    System.out.println(Thread.currentThread().getName());
                }
            }
        }.start();
    }
  • 误区二:如果要定义一个同步方法来体现线程访问的原子性,应该把这个同步方法定义成一个循环。
    我们可能定义了一个同步方法,但是在运行的获得的结果却表明这个方法并不是同步的。就像下面这个方法,虽然有synchronized修饰,但是如果start两个线程来运行这个方法,结果很可能是两个线程交替运行,让人以为synchronized没有起作用。
    实际上,当一个线程在运行fetchB中的代码的时候,另一个线程是无法访问fetchB的,关键字synchronized确实起到了作用。之所以打印的结果会是两个线程交替访问fetchB,而不是一个线程访问完毕后,另一个线程访问,是因为fetchB方法不是一个“持久”的方法。如果把fetchB改成一个循环,那么在一个线程走完fetchB的循环之前,另一个线程是绝对无法访问fetchB的代码的。打印的结果会是一个线程运行完毕后,另一个线程才开始运行。
    synchronized boolean fetchB(String str, int num) {
        if (this.B - num >= 0) {
            this.B -= num;
            System.out.println(str + " fetch B, B = " + this.B);
            return true;
        } else {
            System.out.println(str + " cannot fetch B");
            return false;
        }
    }

Java多线程的争抢问题

线程的运行状态及进度是不可预知的。
通常,我们在写多线程Demo的时候会发现有些操作并不是同步的,然而输出的结果却好像是两个线程在调用同步的方法,给我们一种错觉。
实际上,这种结果是因为线程调度出现了一边倒的情况。比如线程A抢不过线程B,只有当线程B运行完毕后,线程A才能获得CPU,并开始运行。
如果出现了这种情况,可以用Thread.sleep()方法让线程休眠,甚至是让两个线程休眠相同的时间都可以大大降低这种情况出现的概率

public void run2() {
        DeadLock deadLock = new DeadLock();
        new Thread() {
            @Override
            public void run() {
                deadLock.cycleFetch("one", 20);

            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                while (deadLock.fetchB2("two", 2)) {
              
                }
            }
        }.start();
    }

synchronized使用注意事项

synchronized用起来并不简单,不是说我们只要给一个代码块,一个方法,或者是一个对象加了synchronized,就可以高枕无忧的以各种各样的姿势使用多线程并发访问这个代码块/方法/对象了。
要注意的是:只有当多线程访问的是同一个同步代码块/方法/对象,才能实现同步访问。

  • 例一:我们把代码块/方法/对象写在一个类里,结果在线程的run()方法体中实例化了一个对象,结果就是每一个线程有一个独有的对象,无论加不加synchronized,访问的最终结果都是一样的。

  • 例二:我们用synchronized修饰了一个类,对这个类实现了同步。但是这个run3()和DeadLock类中其他的同步方法并不是同步的。也就是说,如果start两个线程,一个运行run3(),另一个运行DeadLock类中的同步方法,并不能实现操作的原子性。

    public void run3(String threadName) {
        synchronized (DeadLock.class) {
            for (int i = 0; i < 20; i++) {
                this.A--;
                System.out.println(threadName + " , " + this.A);
            }
        }
    }

相关文章

网友评论

      本文标题:Java多线程及synchronized用法理解

      本文链接:https://www.haomeiwen.com/subject/ilclsctx.html