为了账号安全,请及时绑定邮箱和手机立即绑定

Java多线程(2):线程关键字

标签:
Java

您好,我是湘王,这是我的慕课手记,欢迎您来,欢迎您再来~



Java中和线程相关的关键字就两volatilesynchronized

volatile以前用得较少以后会用得更少后面解释)。它是一种非常轻量级的同步机制,它的三大特性是

1、保证可见性,即强制CPU高速缓存的数据立即写入主存,会导致其他CPU核中对应的高速缓存内容无效就像这样

https://img1.sycdn.imooc.com//635409790001fee205500332.jpg

 

如果由于同步需要某行代码使用了volatile关键字那么当CPU内核1收到指令时会立即将它位于高速缓存中的数据这里是字符串1写到主存中去那么其余的数据哈哈test9.9)会全部失效

 

2、有序性(禁止指令重排)

所谓指令重排就是对于int a = 0、 int b = 1这类赋值语句,编译成class字节码时,a = 0,b = 1的顺序可能会因为编译器的优化而导致和书写时的顺序不一致但如果有c = a + b,那么这行代码必须在前两句的后面执行这个不用深究知道就好了

3、不保证原子性

所谓不保证原子性就是如果遇到多个线程同时执行i = i + 1,那么i可能就不能保证仅仅被加1

 

根据Java的内存模型整理出如下这些规则看看就好能理解就记住理解不了也没关系不用记

https://img1.sycdn.imooc.com//6354097f0001a88b09410610.jpg

 

下面还是以代码来说明

public class VolatileTest extends Thread {
    static boolean flag = true;

    @Override
    public void run() {
        while (flag) {
        }
        System.out.println("子线程结束");
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(new VolatileTest()).start();
        TimeUnit.SECONDS.sleep(1);
        flag = false;
        System.out.println("主线程结束");
    }
}


加上volatile之后,子线程就不会一直卡住了

public class VolatileTest2 extends Thread {
    volatile static boolean flag = true;

    @Override
    public void run() {
        while (flag) {
        }
        System.out.println("子线程结束");
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(new VolatileTest2()).start();
        TimeUnit.SECONDS.sleep(1);
        flag = false;
        System.out.println("主线程结束");
    }
}


 

其实在第一个类VolatileTest中即使不用volatile关键字也能让程序正常结束只需要添加一行代码就行了

public class VolatileTest3 extends Thread {
    static boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            // 只要在这里随便加一行代码,虽然原理不同,但效果和加了volatile一样
            System.out.println(" ");
        }
        System.out.println("子线程结束");
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(new VolatileTest3()).start();
        TimeUnit.SECONDS.sleep(1);
        flag = false;
        System.out.println("主线程结束");
    }
}


 

所以总结一下适应volatile的条件

1、对变量的写操作不依赖于当前值,就是不会出现 x++ 或 x = x + y 这样的自增性质的语句

2、变量没有包含在具有其他变量的代码中,就是不会出现 if(x > y) { // dosomething; } 这样的语句,因为这样的话,dosomething就不是线程安全的

所以适合使用volatile的场景是

1、状态标记量

2、双重检查

 

状态标记量:只设置简单的状态值,希望立即看到,且无其他变量参与。之前Volatile2中的flag就是这样的状态标记量

双重检查这个有点生僻冷门不深究)。

因为目前synchronized的性能已经有了大幅提升,而且机器硬件的性能也较之前翻了几十倍,volatile存在的意义已经不大了所以如果对volatile的理解不够深入,就干脆不用理解了

 

再来看另一个重量级选手synchronized

synchronized关键字用于解决多个线程之间的资源共享问题,通常有三大作用域

1、静态方法:public synchronized static void methodName(),进入方法前要获得当前类对象的锁

2、实例方法:public synchronized void methodName(),进入方法前要获得当前对象实例的锁

3、代码块:最常用,指定一个特别的加锁对象,进入同步代码块前要获得这个对象锁

这几个就不一个个地说了因为网上这类例子太多了

就简单说一下volatile和synchronized的比较

1、volatile是synchronized的轻量级实现,性能要比synchronized要好

2、volatile只能修饰变量,synchronized只能修饰方法和代码块

3、volatile不会造成阻塞,synchronized会

 

最后再说一个我之前遇到的小问题

如果使用的IDE是Eclipse那么当前活动的线程数量可能是1;

但如果使用的IDE是IDEA那么当前活动的线程数量可能是2

有图为证

https://img1.sycdn.imooc.com//6354099f0001d6cf06690730.jpg

 

这是因为在IDEA中多了一个Monitor Ctrl-Break

https://img1.sycdn.imooc.com//635409a70001e0d013900468.jpg

 

至于这是个啥玩意各位一查便知

 



感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消