java排序命令_初识指令重排序,Java 中的锁

public static void main(String[] args) throwsInterruptedException {int j=0;int k=0;

j++;

System.out.println(k);

System.out.println(j);

}

上面这段代码可能会被重排序:如下

public static void main(String[] args) throwsInterruptedException {int k=0;

System.out.println(k);int j=0;

j++;

System.out.println(j);

}

此时指令的执行顺序可以与代码逻辑顺序不一致,但不影响程序的最终结果.

再看个demo

public classThreadExample2 {static inti;public static boolean runing = true;public static void main(String[] args) throwsInterruptedException {

traditional();

Thread.sleep(100);

runing= false;

}public static voidtraditional() {

Thread thread= newThread() {

@Overridepublic voidrun() {while(runing){

i++;//没有方法,JVM会做指令重排序,激进优化

}

}

};

thread.start();

}

}

执行下main方法

bd9361909b1a569ea4ae866e7560b7b0.png

可以看出该程序一直在跑,不会停止.

此时jvm发现traditional方法内没有其他方法,JVM会做指令重排序,采取激进优化策略,对我们的代码进行了重排序

如下:

static inti;public static boolean runing = true;public static void main(String[] args) throwsInterruptedException {

traditional();

Thread.sleep(100);

runing= false;

}public static voidtraditional() {

Thread thread= newThread() {boolean temp=runing;//注意这里,此时while的条件永远为true

@Overridepublic voidrun() {while(temp){

i++;//没有方法,JVM会做指令重排序,激进优化

}

}

};

thread.start();

}

因此程序不会停止.

我们稍微改动下代码,在while 循环里加个方法

static inti;public static boolean runing = true;public static void main(String[] args) throwsInterruptedException {

traditional();

Thread.sleep(100);

runing= false;

}public static voidtraditional() {boolean temp=runing;

Thread thread= newThread() {

@Overridepublic voidrun() {while (runing){// i++;//没有方法,JVM会做指令重排序,激进优化//有方法,JVM认为可能存在方法溢出,不做指令重排序,保守优化策略

aa();

}

}

};

thread.start();

}public static voidaa(){

System.out.println("hello");

}

看下结果

f1a17c48925f2573b28984da4c738df2.png

可以看出,程序自行停止了,因为有方法,JVM认为可能存在方法溢出,不做指令重排序,采取保守优化策略

runing = false;

全局变量runing 改动值以后,被thread线程识别,while 循环里值变为false,就自动停止了.

ok,继续,我们把main方法中的sleep()注释掉,如下

public static void main(String[] args) throwsInterruptedException {

traditional();//Thread.sleep(100);

runing = false;//会优先执行主线程的代码

}public static voidtraditional() {boolean temp=runing;

Thread thread= newThread() {

@Overridepublic voidrun() {while (runing){// i++;

}

}

};

thread.start();

}

看下结果:

56dd068e24e6db46ac776dbbb865a0a9.png

此时,程序停止了,这是为什么呢:

可能是因为thread 线程和main线程竞争cpu资源的时候,会优先分配给main线程(我不确定,读者们可以自己思考一下)

Java 中的锁

synchronized关键字

在1.6版本之前,synchronized都是重量级锁

1.6之后,synchronized被优化,因为互斥锁比较笨重,如果线程没有互斥,那就不需要互斥锁

重量级锁

1.当一个线程要访问一个共享变量时,先用锁把变量锁住,然后再操作,操作完了之后再释放掉锁,完成

2.当另一个线程也要访问这个变量时,发现这个变量被锁住了,无法访问,它就会一直等待,直到锁没了,它再给这个变量上个锁,然后使用,使用完了释放锁,以此进行

3.我们可以这么理解:重量级锁是调用操作系统的函数来实现的锁--mutex--互斥锁

以linux为例:

1.互斥变量使用特定的数据类型:pthread_mutex_t结构体,可以认为这是一个函数2.可以用pthread_mutex_init进行函数动态的创建 : int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)3.对锁的操作主要包括加锁 pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个

3.1 int pthread_mutex_tlock(pthread_mutex_t *mutex) 在寄存器中对变量操作(加/减1)3.2 int pthread_mutex_unlock(pthread_mutex_t *mutex) 释放锁,状态恢复3.3 int pthread_mutex_trylock(pthread_mutex_t *mutex)

pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待

函数pthread_mutex_trylock会尝试对互斥量加锁,如果该互斥量已经被锁住,函数调用失败,返回EBUSY,否则加锁成功返回0,线程不会被阻塞

偏向锁

偏向锁是synchronized锁的对象没有资源竞争的情况下存在的,不会一直调用操作系统函数实现(第一次会调用),而重量级锁每次都会调用

看个demo

public classSyncDemo2 {

Object o= newObject();public static voidmain(String[] args) {

System.out.println("pppppppppppppppppppppp");

SyncDemo2 syncDemo= newSyncDemo2();

syncDemo.start();

}public voidstart() {

Thread thread= newThread() {public voidrun() {while (true) {try{

Thread.sleep(500);

sync();

}catch(InterruptedException e) {

}

}

}

};

Thread thread2= newThread() {

@Overridepublic voidrun() {while (true) {try{

Thread.sleep(500);

sync();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

};

thread.setName("t1");

thread2.setName("t2");//两个线程竞争时,synchronized是重量级锁,一个线程时,synchronized是偏向锁

thread.start();

thread2.start();

}//在1.6版本之前,synchronized都是重量级锁//1.6之后,synchronized被优化,因为互斥锁比较笨重,如果线程没有互斥,那就不需要互斥锁

public voidsync() {synchronized(o) {

System.out.println(Thread.currentThread().getName());

}

}

}

代码很简单,就是启动两个线程,并且调用同一个同步方法,看下结果

a358e86c7299807b89b98883c5a18996.png

可以看到,两个线程都执行了该同步方法,此时两个线程竞争,synchronized是重量级锁

我们把一个线程注释掉

//两个线程竞争时,synchronized是重量级锁,一个线程时,synchronized是偏向锁

thread.start();//thread2.start();

看下结果:

6deff6a817ebf9ad4a9ec79341c3dd04.png

此时synchronized是偏向锁

那么怎么证明呢:我目前没那个实力,给个思路.

1.需要编译并修改linux源码函数pthread_mutex_lock(),在函数中打印当前线程的pid

2.在同步方法中打印语句"current id"+当前pid(需要自己写c语言实现),java的Thread.currentThread().getId()不能获取操作系统级别的pid

3.两个线程竞争时,执行一次

2e09a60f5c53eef2eefb2b72e4748ecb.png

说明是重量级锁,因为每次都调用操作系统的函数pthread_mutex_lock()来实现

4.注释掉一个线程,再执行一次

18f9f33f01d4a73762260732ec41cec2.png

说明是偏向锁,因为第一次会调用pthread_mutex_lock(),后面就不调用系统函数了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值