Java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时,将会导致数据不准确,相互之间产生冲突,因此,实现线程同步以避免在该线程没有完成操作之前,被其他线程调用,从而保证了该变量的唯一性和准确性。一般情况下,实现线程同步有以下3种方法:
1)Synchronized修饰整个方法
例:
public synchronized void set(){}
注意:synchronized也可以修饰静态方法,如果此时调用这个方法,将会锁住整个类。
2) synchronized修饰具体的代码块
public void save(){
synchronized (this){
count += money
}
}
PS : 同步是一种高开销的操作,因此,应该尽量减少同步的内容。一般情况下不需要同步整个方法,只需同步对应的代码块即可。
小编认为Lock与Synchronized比较起来,Lock的优点还是比较多的。
如果一个代码块被synchronized关键字修饰,当一个线程执行该代码块时,其他的线程便只能一直等待直到占有锁的线程释放锁。那么,什么情况下占有锁的线程会释放锁呢 ?
既然synchronized如此方便,可以轻松实现对资源的同步互斥访问。那么,为什么还需要Lock呢 ?考虑以下3种情况:
Case 1 :
在使用synchronized关键字时,假如占有锁的线程由于要等待IO或者其他原因(比如调用sleep()方法)被阻塞了,但是又没有使释放锁,那么其他的线程就只能一直等待。
Case 2:
多个线程读写文件时,读和写操作会发生冲突,写和写操作也会发生冲突,但是,读和读操作不会发生冲突。所以使用synchronized关键字就会导致一个问题:无法实现多个线程同步进行读操作。这就会极大的影响程序的执行效率。
Case 3:
我们可以通过Lock得知线程有没有成功获取锁,但是synchronized无法做到。
以上三个Case中所描述的问题,Lock都可以解决,现在知道为什么会有Lock 这个东西了吧哈哈!
下面我们简单来说一下使用Lock实现线程同步的方法:
lock.lock() 加锁;lock.unlock() 释放锁。
public class Count{
Lock lock = new ReentrantLock();
public void save(){
for(int i=0;i<10;i++)
{ count++;
System.out.println("count:"+count);
}
lock.unlock();
}
}
1)volatile是变量修饰符,其修饰的变量具有可见性。
可见性就是说一旦某个线程修改了被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取的时候,可以立即获取修改之后的值。
在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存。
2)volatile可以禁止指令重排
指令重排是指编译器或者CPU为了提高程序的运行效率,可能会对输入的代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码中的执行结果是一致的,应用条件是单线程条件,对于并发多线程的场景下,指令重排会产生不确定的结果。
想要了解更多的线程相关的知识,我们可以观看动力节点在线的Java多线程视频课程,名师讲解,帮助我们攻克Java多线程的难关。
提枪策马乘胜追击04-21 20:01
代码小兵92504-17 16:07
代码小兵98804-25 13:57
杨晶珍05-11 14:54