浅谈java线程安全

在了解线程安全之前,有必要谈谈线程的工作原理,我本人的理解是这样的:

jvm中有主存,Java中所有变量都储存在主存中,对于所有线程都是共享的.同时每个线程拥有自己独有的工作内存,当一个线程对一个变量进行操作时,都要在自己的工作内存中建立该变量的一个副本,操作完之后再写入主内存.线程之间无法相互直接访问,变量传递均需要通过主存完成.

因此多个线程同时操作同一个变量时,就可能会出现不可预知的结果.

解决该问题我们第一个想到的就是synchronized关键字,synchronized具有原子性和可见性两个重要的特性.

1.原子性

所谓原子性就是当一个线程对一份代码进行操作时,不会被线程调度机制打断,一旦开始操作,就一直运行到结束.也就是说当该线程对一段代码进行操作时,不会有其他线程来打断它.

2.可见性

可见性就是指当一个线程修改了线程共享变量的值,其它线程能够立即得知这个修改.很明显单个线程在各自独立工作内存修改共享变量的副本后,会将新值同步回主内存,这样其他线程再次访问主存就能感知到共享变量的变化.就起到了可见性作用(后面会提到volatile关键字,用于保证变量的可见性).
synchronized的作用是建立一个监视器(monitor),也就是线程所持有的锁,当线程操作一份代码时,通过加锁来实现线程安全,每个线程只有获得锁之后,执行完"加载到工作内存->操作->写入到主存"的过程,才会释放锁.
所以线程不安全就是如果多线程访问同一份代码,得不到我们预期的结果.线程安全的实现依靠线程同步.
下面探讨synchronized关键字的用法:
synchronized可修饰普通方法,静态方法,或者代码块.
每个java对象都可以用做一个实现同步的锁,这些锁称为内置锁.同一个对象的锁最短只有一个线程能够获得该锁.
下面分析如下代码:

synchronized修饰方法:
1.对象锁的synchronized修饰方法和代码块.

public synchronized void test1(){
    操作.....
}

public void test2{
    synchronized(this){
	代码块...
    }
}
第一种方法默认锁为当前对象,第二个方法用了同步代码块方式,传入的对象实例是this,表明是当前对象,所以两个同步代码所需要获得的对象锁都是 同一个对象锁.
当然第二个方法也可传入其他对象的实例,用于不同的对象锁.
2.类锁的synchronized修饰(静态)方法和代码块:

public static synchronized void test1(){
    操作.....
}  

public void test2{
    synchronized(TestSynchronized.class){
	代码块...
    }
}
因为类锁只是一个抽象出来的概念,只是为了区别静态方法的特点,因为静态方法是所有对象实例共用的,所以对应着synchronized修饰的静态方法的锁也是唯一的,所以抽象出来个类锁.


类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。




你可能感兴趣的:(java)