在上一篇文章讲了synchronized的实现原理,今天来讲讲它的应用场景。
基础使用
1,基础使用
synchronized 锁定的不是代码块,是对象。注意如果这个对象不是唯一的则可能加锁失败,因为synchronized可以锁定的对象有两种1,类的实例;2,类的对象。具体用法
synchronized (object){
count--;
log.debug(Thread.currentThread().getName() + " count = " + count);
}
而如果使用synchronized在方法上修饰,相等于锁的是当前实例的对象。其实这三种写法是一样的。
public void test(){
//synchronized(this)锁定的是当前类的实例,这里锁定的是Demo2类的实例
synchronized (this){
count--;
log.debug(Thread.currentThread().getName() + " count = " + count);
}
}
public synchronized void test(){
//synchronized(this)锁定的是当前类的实例,这里锁定的是Demo2类的实例
count--;
log.debug(Thread.currentThread().getName() + " count = " + count);
}
synchronized (Demo2.class){
count--;
log.debug(Thread.currentThread().getName() + " count = " + count);
}
2,使用原则
1,在使用加锁对象时,不要选择改变了的对象。例如锁定对象o,但是如果o变成了另外一个对象则锁的对象发生了改变,会影响程序运行。
2,避免使用字符串常量作为锁对象
3,加锁尽量在越少的代码块进行
3,脏读问题
当多个线程对一个对象进行读写容易造成脏读问题
4,重入
synchronized 默认支持对一个对象重复加锁,成为锁的重入。类的继承也会使锁重入。
synchronized void test1(){
log.debug("test1 start.........");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
test2();
}
/**
* 为什么test2还需要加sync
*
* 他本身就包含在test1 而test1已经加了sync
*/
synchronized void test2(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("test2 start.......");
}
5,异常
如果加锁过程中有抛出异常,则不会释放锁,如果没有做try catch,则会释放锁。
synchronized (o) {
//t1进入并且启动
log.debug(Thread.currentThread().getName() + " start......");
//t1 会死循环 t1 讲道理不会释放锁
while (true) {
count++;
log.debug(Thread.currentThread().getName() + " count = " + count);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//加5次之后 发生异常
/**
* 如果程序发生异常如果没有try 则会释放锁
* 反之不会释放锁
*/
if (count == 5) {
int i = 1 / 0;
}
}
}
6,volatile的可见性
/**
* volatile 关键字,使一个变量在多个线程间可见
* mian,t1线程都用到一个变量,java默认是T1线程中保留一份副本,这样如果main线程修改了该变量,
* t1线程未必知道
*
* 使用volatile关键字,会让所有线程都会读到变量的修改值
*
* 在下面的代码中,running是存在于堆内存的t对象中
* 当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个副本,
* 并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行
*
*
* 但是这可能是个错误
* 关于这个例子 在后面会专门花时间再讲
*/
@Slf4j(topic = "enjoy")
public class Demo {
boolean running = true;
List<String> list = new ArrayList<>();
/**
* t1线程
*/
public void test(){
log.debug("test start...");
boolean flag =running;
while (running){
}
log.debug("test end...");
}
public static void main(String[] args) {
Demo demo = new Demo();
new Thread(demo :: test,"t1").start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo.running = false;
}
}
7,volatile 不保证原子性,而synchronized保证
//保证了可见性,不保证原子性,仍然可能错误的加法
volatile int count = 0;
public void test(){
for (int i = 0; i < 10000; i++) {
count ++;
}
}
//synchronized 保证了原子性和可见性
public synchronized void test(){
for (int i = 0; i < 10000; i++) {
count ++;
}
}