Java工作线程 主存 同步机制_Java 并发变成同步机制

并发编程的演进:

批处理——多进程——多线程

在多线程变成中,由于多个线程共享进程的变量,有可能出现同时访问一个资源的情况,因此需要使用同步机制。

java的内存模型:

Java内存模型规定所有的变量都存在主存当中,每个线程都有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行,而不能直接在主存进行操作。并且每个线程不能访问其他线程的工作内存。

关键字:

原子操作:原子为不可再分操作。只有对象的读取和赋值是原子操作。int i=10 是原子操作。int x = y;不是原子操作

violation:可见关键字

Synchronized:内部隐示锁

lock: ReentrantLock(显示锁) + ReentrantReadWriteLock(读写锁)

violation关键字:

一旦一个共享变量被volatile修饰之后,那么就具备了两层含义。

1)可见性:  保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

它会强制将对缓存的修改操作立即写入主存,同时会导致其他CPU对应的缓存无效。

通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中

2) 有序性: 禁止进行指令重排序。

需要注意的是violation不保证原子性。

比如:

publicvolatileintinc =0;

inc++;

在多线程中不同依靠violation关键字保证原子操作。

使用volatile关键字的场景:

1) 多变量的写操作不依赖于当前值

2) 该变量没有包含在具有其他变量的不变式中。

使用例子:状态标记量:

volatile boolean inited = false;

//线程1:

context = loadContext();

inited = true;

//线程2:

while(!inited ){

sleep()

}

doSomethingwithconfig(context);

synchronized:

把一些不是原子操作组合成原子操作。

(1) synchronized方法

示例代码:

public class InsertData {

public synchronized void insert(){

for(int i=0; i<10;++i){

System.out.println(Thread.currentThread().getName() + ": 执行,变量i = "+i);

}

}

public synchronized void insert2() {

System.out.println(Thread.currentThread().getName()+ "start inset2");

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+ "end inset2");

}

public static synchronized void insert3(){

System.out.println(Thread.currentThread().getName() + "insert3");

}

public void insert4(){

System.out.println(Thread.currentThread().getName()+"insert4");

}

}测试代码:

public class SynTest {

@Test

public void test() throws InterruptedException {

InsertData insertData = new InsertData();

new Thread(new Runnable() {

@Override

public void run() {

insertData.insert();

}

}).start();

new Thread(new Runnable() {

@Override

public void run() {

insertData.insert();

}

}).start();

Thread.sleep(8000);

}

}需要注意的是:

1)当一个线程正在访问一个对象的synchronized方法,那么其他 线程不能访问该对象的synchronized方法,这个原因很简单,是因为对当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized方法。

2) 当一个线程正在访问对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。

3) 如果该方法是非static的,则该锁是对象锁。如果方法是static的,则该锁是类锁。对象锁和类锁不互斥。

4) 对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。

(2) synchronized 代码块

synchronized(synObject){

}

当在某个线程中执行这段代码块,该线程会获取对象synObject的锁,从而使其他线程无法同时访问该代码块。

synObject可是是this,代码获取当前对象的锁,也可以是类中的一个属性,也可以是方法的一个object类型的入参,代表获取该对象的锁。

synchronized代码块使用起来比synchronized方法要灵活得多。如果一个方法中只有一部分代码需要同步,如果此时对整个方法用synchronized进行同步,会影响执行效率。而使用synchronized代码块就可以避免这个问题。

synchronized的缺陷:

当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只有两种情况:

1) 获取锁的线程执行完了代码块,然后线程释放对应锁的占有。

2) 线程执行发生异常,此时JVM会让线程自动释放锁。

那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程变只能干巴巴地等待,严重影响程序执行效率。所以就需要有一种机制可以不让等待的线程一直无限地等待下去(比如只等待一定时间或者能够相应中断),通过lock就可以办到。

Lock

1) ReentrantLock:

使用事例:

采用lock,必须主动释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被释放,防止死锁的发生。

Lock lock =new ReentrantLock();

lock.lock();

try{

//执行业务逻辑代码

}catch (Exception e){

}finally {

lock.unlock();

}

2) ReentrantReadWriteLock: 读写锁

readLock用来获取读锁,writeLock用来获取写锁。

读锁可以被多个线程占有。

一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。

一个线程已经占用了写锁,则其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。

Lock和synchronized的选择:

总结来说,Lock和synchronized有以下几点不同:

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

lock是可中断的锁,而synchronized是不可中断锁。lock只能中断等待锁的线程,不能中断正在执行的线程。

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

相同:

1) synchronized和lock都是可重入的锁。

参考资料:http://www.cnblogs.com/dolphin0520/p/3920373.html

http://www.cnblogs.com/dolphin0520/p/3923737.html

http://www.cnblogs.com/dolphin0520/p/3923167.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值