synchronized锁
解决线程同步机制的并发问题要用排队:队列 + 锁
两个线程同时争抢一个对象,可能会造成数据更新不及时.如两个人同时在一个银行账户取钱,钱取负数了
synchronized的两种用法
-
用作修饰符时锁的是this(当前对象)
-
作用就是锁住这个对象
-
直到对象结束才释放让别人可以拿到这个锁
-
synchronized同步块锁变量时
-
只有等这个变量操作完成才会释放
-
当是由多条线程运行的多个线程操作一个变量的时候用这种方式
-
所以一般用来锁住会变化的变量
银行取钱案例:多个线程同时操作同一个对象,造成数据出错后用synchronized锁来解决
思想
用一个static变量模拟银行账号的唯一性,同时有两个线程都来取钱
package com.li.changGe.multithreading.threadSafety;
public class SynchronizedDemo01 extends Thread{
public static void main(String[] args) {
SynchronizedDemo01 synchronizedDemo01 = new SynchronizedDemo01(100);
SynchronizedDemo01 synchronizedDemo011 = new SynchronizedDemo01(100);
/**
* 继承Thread和实现Runnable本质上没有区别
* 唯一的区别是继承Thread不强求实现run方法
* Thread已经实现了,但是不代表不需要重写run方法
* Thread底层的target初始为空
*
* 归根结底只是java的继承和实现的区别
*
* 不过如果要实现一个对象运行两条线程建议用new Thread这种方式
* 用同一个对象同时运行两个.start会报错:
* IllegalThreadStateException(非法线程状态异常)
*/
new Thread(synchronizedDemo01,"a").start();
new Thread(synchronizedDemo011,"b").start();
//new Thread(synchronizedDemo01,"b").start();
//synchronizedDemo01.start();
//synchronizedDemo01.start();
}
/**
* 实现Runnable及其子类Thread后,idea为其在父类构造方法基础扩展属性
* 构造方法不能被继承(自动调用):你继承Thread时能点出.Thread()方法吗?
*/
public SynchronizedDemo01(Runnable target, double putMoney) {
super(target);
this.putMoney = putMoney;
}
/**
* static修饰的对象只有一个
* 就像银行的账号是唯一性的.
*
* synchronized不允许传入基本类型
* 用包装类底层会自动拆箱和装箱
*/
private static Double balance = 100.0;
public SynchronizedDemo01(double putMoney) {
this.putMoney = putMoney;
}
/**
* 取款数不固定
* 后来的人会把前面人的取钱数覆盖
*/
private /*static*/ double putMoney;
@Override
public void run() {
putMoney(putMoney);
}
/**
* 多个线程同时操作一个对象:
* 两个人同时从一个账号中取钱
*
* 每个线程都会在自己的工作内存中交互:得到数据后放入一个自己(线程)中的一个空间操作
* 且sleep时不会释放锁
* 造成余额出现负数的情况
*/
/**
* 每个对象都有一把锁
* synchronized用作修饰符时锁的是this(当前对象)
* 它的作用就是锁住这个对象,直到对象结束才释放让别人可以拿到这个锁
*/
public /*synchronized*/ void putMoney(double putMoney){
/**
* synchronized同步块锁变量时,
* 只有等这个变量操作完成才会释放
*
* 当是由多条线程运行的多个线程操作一个变量的时候用这种方式
* 所以一般用来锁住会变化的变量
*/
/**
* 锁方法会有弊端:
* 一段代码中,A段代码只负责读,B段代码只负责写
* 方法被锁住后,读写不分离了.
*/
synchronized (balance) {
/**
* 因为继承了Thread
* 就可以直接使用Thread的getName()方法
*/
System.out.println(this.getName()+"进来取钱了,余额为"+ balance);
if (balance < putMoney) {
System.out.println("钱不够了");
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
balance -= putMoney;
System.out.println(Thread.currentThread().getName() + "取走" + putMoney
+ ",还有" + balance);
}//else
return;
}//synchronized
}//putMoney
}