• 92409

    文章

  • 775

    评论

  • 17

    友链

  • 最近新加了换肤功能,大家多来逛逛吧~~~~
  • 喜欢这个网站的朋友可以加一下QQ群,我们一起交流技术。

第二章-对象及变量的并发访问-第二篇

撸了今年阿里、腾讯和美团的面试,我有一个重要发现.......>>

锁对象的改变

请阅读如下代码

public class MainClass {
    private String lock = "123";
    public void printStringB() {
        try {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName()+"begin " +System.currentTimeMillis());
                lock="456";
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+"end " +System.currentTimeMillis());
            }
        } catch ( InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

public class ThreadA extends Thread {
    private MainClass mainClass;

    public ThreadA(MainClass mainClass) {
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
            mainClass.printStringB();
    }
}


public class ThreadB extends Thread {
    private MainClass mainClass;

    public ThreadB(MainClass mainClass) {
        this.mainClass = mainClass;
    }

    @Override
    public void run() {
        mainClass.printStringB();
    }
}

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        ThreadA threadA = new ThreadA(mainClass);
        threadA.setName("a");
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass);
        threadB.setName("b");
        Thread.sleep(50);
        threadB.start();

    }
}    

输出结果是

abegin 1534849131558
bbegin 1534849131607
aend 1534849133559
bend 1534849133608

因为线程a,b持有的锁是不同的锁,所以会异步执行,若改为如下所示

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        ThreadA threadA = new ThreadA(mainClass);
        threadA.setName("a");
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass);
        threadB.setName("b");
        //Thread.sleep(50);
        threadB.start();

    }
}    

线程将会同步执行,因为同一时刻都是争抢字符串“123”对象锁。 还需要提示一下,只要对象不改变,即使对象的属性发生变化运行的结果还是同步的。 看如下代码

public class UserInfo
{
    int a;
    int b;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }
}

public class MainClass {
    public void printStringB(UserInfo userInfo) {
        try {
            synchronized (userInfo) {
                System.out.println(Thread.currentThread().getName()+"begin " +System.currentTimeMillis());
                userInfo.setA(12);
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+"end " +System.currentTimeMillis());
            }
        } catch ( InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

public class ThreadA extends Thread {
    private MainClass mainClass;
    private UserInfo userInfo;

    public ThreadA(MainClass mainClass, UserInfo userInfo) {
        this.mainClass = mainClass;
        this.userInfo = userInfo;
    }

    @Override
    public void run() {
        mainClass.printStringB(userInfo);
    }
}

public class ThreadB extends Thread {
    private MainClass mainClass;
    private UserInfo userInfo;

    public ThreadB(MainClass mainClass, UserInfo userInfo) {
        this.mainClass = mainClass;
        this.userInfo = userInfo;
    }

    @Override
    public void run() {
        mainClass.printStringB(userInfo);
    }
}

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        UserInfo userInfo = new UserInfo();
        ThreadA threadA = new ThreadA(mainClass,userInfo);
        threadA.setName("a");
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass,userInfo);
        threadB.setName("b");
        //Thread.sleep(50);
        threadB.start();

    }
}

输出结果

abegin 1534849856936
aend 1534849858937
bbegin 1534849858937
bend 1534849860937

稍微运行函数

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        UserInfo userInfo = new UserInfo();
        ThreadA threadA = new ThreadA(mainClass,userInfo);
        threadA.setName("a");
        threadA.start();
        ThreadB threadB =new ThreadB(mainClass,userInfo);
        threadB.setName("b");
        Thread.sleep(1000);
        threadB.start();

    }
}

输出结果还是

abegin 1534849955552
aend 1534849957552
bbegin 1534849957552
bend 1534849959552

足以证明即使对象属性发生变化了也不会影响线程同步。

volatile关键字

volatile关键字的作用就是是变量在多个线程之间可见,其作用就是是线程强制从公共对战中取得变量的值,而不是从线程中私有数据栈取得变量的值。

public class ThreadA extends Thread {
    private boolean isRunning = true;

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean running) {
        isRunning = running;
    }

    @Override
    public void run() {
        System.out.println("进入run了");
        while (isRunning) {}
        System.out.println("线程被停止了");
    }
}

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
            try {
                ThreadA threadA = new ThreadA();
                threadA.start();
                Thread.sleep(1000);
				threadA.setRunning(false);
                System.out.println("已经被赋值了false");
            } catch (InterruptedException ex){
                ex.printStackTrace();
            }
    }
}

输出结果

进入run了
已经被赋值了false

程序一直在运行,无法停止。

程序稍微修改以后

public class ThreadA extends Thread {
    volatile private boolean isRunning = true;

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean running) {
        isRunning = running;
    }

    @Override
    public void run() {
        System.out.println("进入run了");
        while (isRunning) {}
        System.out.println("线程被停止了");
    }
}

输出结果

进入run了
线程被停止了
已经被赋值了false

通过使用关键字,强制的从内存中读取变量的值。

synchronized和volatile比较

  • 字volatile是线程同步的轻量实现,所以volatile的性能比synchronized性能要好。volatile只能修饰变量,而synchronized可以修饰方法,代码块,随着jdk的发布,synchronized性能得到了很大的提升,所以开发中使用的比率是比较大的。
  • 多线程访问volatile不会发生阻塞,synchronized会发生阻塞。
  • volatile保证数据的可见性但是不能保证原子性。synchronized可以保证原子性,也可以间接保证可见性。

volatile非原子性

先看程序

public class ThreadA extends Thread {
    volatile public static int count;

    public static void addCount() {
        for (int i = 0; i < 100; i++) {
            count++;
        }
        System.out.println("count = " + count);
    }

    @Override
    public void run() {
        addCount();
    }
}

public class RunClass {
    public static void main(String[] args) {
        ThreadA[] threadA = new ThreadA[100];
        for (int i = 0; i < 100; i++) {
            threadA[i] = new ThreadA();
        }
        for (int i = 0; i < 100; i++) {
            threadA[i].start();
        }
    }
}

输出结果

....
count = 6767
count = 6667
count = 6567
....

整个程序是异步执行的,不具备同步作用。程序稍微修改以后如下

public class ThreadA extends Thread {
    volatile public static int count;

    synchronized public static void addCount() {
        for (int i = 0; i < 100; i++) {
            count++;
        }
        System.out.println("count = " + count);
    }

    @Override
    public void run() {
        addCount();
    }
}

程序输出结果如下

...
count = 9800
count = 9900
count = 10000

这次运算准确,具备同步作用。

volatile的作用

volatile的作用主要是用于在多个线程中可以感知实例变量被更改了,并且可以获得最新值的使用,也就是多线程读取共享变量的时候可以获取最新值。

原子类也并不完全安全

原子类在具有逻辑的情况下输出结果也是具有随机性的,看如下代码

public class ThreadA extends Thread {
    public static AtomicLong count = new AtomicLong();

    public static void addCount() {
        count.addAndGet(1);
        System.out.println(Thread.currentThread().getName() + "+100以后是"+count.addAndGet(100));
    }

    @Override
    public void run() {
        addCount();
    }
}

public class RunClass {
    public static void main(String[] args) {
        ThreadA[] threadA = new ThreadA[100];
        for (int i = 0; i < 100; i++) {
            threadA[i] = new ThreadA();
        }
        for (int i = 0; i < 100; i++) {
            threadA[i].start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

输出结果

...
Thread-59+100以后是9899
Thread-62+100以后是9999
Thread-61+100以后是9798
...

再稍微修改一下代码

public class ThreadA extends Thread {
    public static AtomicLong count = new AtomicLong();

    synchronized public static void addCount() {
        count.addAndGet(1);
        System.out.println(Thread.currentThread().getName() + "+100以后是"+count.addAndGet(100));
    }

    @Override
    public void run() {
        addCount();
    }
}

输出结果

...
Thread-9+100以后是9999
Thread-22+100以后是10100

出现上述情况的原因是因为方法和方法之间的调用是异步的,所以需要加上同步才行。

synchronized有volatile同步的功能

请看如下代码

public class MainClass {
    private boolean isContinue = true;

    public void runMethod() {
        while (isContinue) {
        }
        System.out.println("程序停止下来了..");
    }

    public void stopMethod() {
        isContinue = false;
    }
}

public class ThreadA extends Thread {
   private MainClass mainClass;
    public ThreadA(MainClass mainClass) {
        this.mainClass = mainClass;
    }
    @Override
    public void run() {
        mainClass.runMethod();
    }
}

public class ThreadB extends Thread {
    private MainClass mainClass;
    public ThreadB(MainClass mainClass) {
        this.mainClass = mainClass;
    }
    @Override
    public void run() {
        mainClass.stopMethod();
    }
}

public class RunClass {
    public static void main(String[] args) throws InterruptedException {
        MainClass mainClass = new MainClass();
        ThreadA threadA = new ThreadA(mainClass);
        threadA.start();
        Thread.sleep(1000);
        ThreadB threadB = new ThreadB(mainClass);
        threadB.start();

    }
}

这个程序不会输出任何东西,代码稍作修改如下

package chapter2;

import java.util.Timer;

/**
 * @program: demo
 * @description:
 * @author: lee
 * @create: 2018-08-20
 **/
public class MainClass {
    private boolean isContinue = true;

    public void runMethod() {
        String anyString = new String();

        while (isContinue) {
            synchronized (anyString){}
        }
        System.out.println("程序停止下来了..");
    }

    public void stopMethod() {
        isContinue = false;
    }
}

程序输出如下

程序停止下来了..

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或者某一个代码块它含了两个特性,互斥性和可见性,同步synchronized不仅可以解决一个线程看到的对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程都看到由同一个锁保护之前的修改效果。


 转载至链接:https://my.oschina.net/u/3798913/blog/1931711


695856371Web网页设计师②群 | 喜欢本站的朋友可以收藏本站,或者加入我们大家一起来交流技术!

欢迎来到梁钟霖个人博客网站。本个人博客网站提供最新的站长新闻,各种互联网资讯。 还提供个人博客模板,最新最全的java教程,java面试题。在此我将尽我最大所能将此个人博客网站做的最好! 谢谢大家,愿大家一起进步!

转载原创文章请注明出处,转载至: 梁钟霖个人博客www.liangzl.com

0条评论

Loading...


发表评论

电子邮件地址不会被公开。 必填项已用*标注

自定义皮肤
注册梁钟霖个人博客