学习笔记-synchronized

1.可见性:一个线程对于共享变量的修改,能够及时被其他线程看到。

那什么是共享变量呢?

答:一个变量在多个线程的工作内存中存在副本,则这个变量就共享变量。

工作内存?

答:Java Memory Model(Java内存模型),简称JMM。

JMM描述了Java程序中各种变量(共享变量)的访问规则以及JVM中将变量存储到内存和从内存中读取变量这样的底层细节。

 

由上图可知,线程并不能与主内存直接交互!!!,必须通过线程自己的工作内存交互。且线程彼此之间也不可交互!!!

2.怎么做才能实现共享变量的可见性呢?

    1>将一个线程改变的共享变量及时刷新到主内存中

    2>其他线程能够及时将主内存中最新的共享变量及时刷新到自己的工作内存中

3.实现方法

JAVA在语言层面支持可见性实现方法:synchronized 和 volatile

首先学习下synchronized:

                     互斥锁:能够保证任何时刻只有一个线程执行锁內的代码。

                     可实现 原子性和可见性。

 

JMM关于synchronized有两条规定:

         1>线程解锁前(退出synchronized代码块之前),必须将共享变量的最新值更新到主内存中;

         2>线程在加锁时,必须首先清空工作内存中的共享变量值,使用共享变量时需要从主内存中读取最新的值。

注意:加锁和解锁需是同一把锁。

线程在解锁前对共享变量的修改在下次加锁前对其他线程可见。

重排序:代码执行的顺序与我们书写的顺序不同,指令重排序是编译器或处理器为了提高程序性能而做的优化。‘

             1>编译器优化的重排序(编译器优化)

             2>指令级并行重排序(处理器优化)

             3>内存系统的重排序(处理器优化)

as-if-serial:无论如何重排序,程序执行的结果应该是与代码顺序执行的结果一致。

JAVA编译器,运行时和处理器都会保证JAVA在单线程下遵循as-if-serial语义。

int num1 = 1;
int num2 = 2;
int sum = num1 + num2;

单线程:第一行、第二行的顺序可以重排,但是第三行不可以。

 

导致共享变量在线程间不可见的原因:

       <<线程的交叉执行

      <<重排序结合线程的交叉执行

      <<共享变量更新后没有及时刷新到工作内存和主内存中

怎么解决上述问题呢?

使用互斥锁(synchronized),保证在某一时间段只有一个线程执行锁內的代码,只有锁释放后,其他线程才可执行。但是,要知道的是,在一个线程执行互斥锁內的代码时,并不影响其他线程执行非锁內的代码。

而且就算不加互斥锁,线程对于共享变量的更新也可能会及时刷新到主内存中去,因为有编译器的优化,很可能在执行多次的情况下,只有一次没有刷新到主内存中去,但是很有可能会因为这一次出现严重的问题。

public class aaa{
     public boolean ready = false;
     public int number = 2;
     public int result = 0;
     public void read(){
        ready = true;    //1.1
        number = 4;      //1.2
     }
     public void wirte(){
        if(ready) {
           number = number * 3;   //2.1
        }
     System.out.println("result="+result);  //2.2
     class ReadWirteThread extends Thread {
 
 	private boolean flag;
	public ReadWirteThread(boolean flag){
		this.flag = flag;
	}	
	public void run(){
		if(flag){
			wirte();
		}else{
			read();
		}
	}
     }
	public static void main(String args[]){
		aaa a = new a();
		a.new ReadWirteThread(false).start();
		a.new ReadWirteThread(true).start();
	}
}

 

输出结果:12 (0)

执行顺序:1.1-1.2-2.1-2.2  输出结果:12

由于每次执行程序时,cpu给每个线程分配的时间长短不同,很有可能先执行wirte()方法,输出结果则为0。

那么如何解决呢?看下面

public synchronized void read(){......}
public void wirte(){......}
虽然给read()方法加了互斥锁,只能保证只有这一个线程在执行此方法且不会中断,但是这并不影响另一个线程执行wirte方法。所以输出结果:12(0)

public synchronized void read(){......}
public synchronized void wirte(){......}
虽然给两种方法都加了互斥锁,但是结果不是12就是0,这是为什么呢?加锁只是为了在执行某段代码时不被中断而去执行其他代码,比如说只会把1.1和1.2执行完毕后才会去执行其他的代码。由于cpu给每个线程分配的时间不同,在结果为12时,cpu让read()方法先执行了,在结果为0时,cpu让wirte()方法先执行了,所以说,虽然代码输出的结果一样,但意义不同。

看下面的代码,可以让线程睡眠20s,给予时间让线程把代码执行完毕。

public void read(){......}
public void wirte(){......}
a.new ReadWirteThread(false).start();
Thread.sleep(1000);
a.new ReadWirteThread(true).start();





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值