要实现线程同步的原因:
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时,
会产生冲突,使得变量值不唯一,因此我们需要加入同步锁来避免在当前线程的操作未完成前,其它线程改变共享资源值的情况发生,从而保证变量的唯一
性和准确性
方法
- synchronized
synchronized可以修饰方法,但是由于同步本身就是一种高开销的操作,因此我们应该尽可能的减少同步的内容,提高性能
使用同步代码块,下面的方式
synchronized (this){
if (account.getBalance() > money) {
System.out.println(Thread.currentThread().getName() + "取钱" + money + "成功");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
account.reduceBalance(money);
System.out.println("\t" + Thread.currentThread().getName() + "成功后的余额: " + account.getBalance());
} else {
System.out.println(Thread.currentThread().getName() + "取钱失败");
System.out.println("\t" + Thread.currentThread().getName() + "失败后的余额: " + account.getBalance());
break;
}
}
- volatile
volatile关键字保证了每个线程所取得的共享资源变量的值是一样的,它的原理是被volatile修饰的变量被访问时,都会从内存中读取,而不是从缓存(寄存器)中,这样保证了每个线程访问到的变量值的一致性
但是他不会保证操作的原子性,
private volatile double balance;
- java.util.concurrent包下的ReentrantLock类
使用的方法
//需要声明这个锁
private Lock lock = new ReentrantLock();
// 存钱
public void addMoney(int money) {
lock.lock();//上锁
try{
count += money;
System.out.println(System.currentTimeMillis() + "存进:" + money);
}finally{
lock.unlock();//解锁
}
}
Lock与Synchronized的对比:
主要相同点:Lock能完成Synchronized所实现的所有功能。
主要不同点:Lock有比Synchronized更好的性能。Synchronized会自动释放锁,但是Lock一定要求程序员手工释放,并且必须在finally从句中释放。
synchronized 修饰方法时 表示同一个对象在不同的线程中 表现为同步队列
如果实例化不同的对象 那么synchronized就不会出现同步效果了。
这个地方不理解可以看这篇博文
https://blog.youkuaiyun.com/jzy23682891/article/details/7197115
- ThreadLocal
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。现在明白了吧,原来每个线程运行的都是一个副本,也就是说存钱和取钱是两个账户,知识名字相同而已。
这样的话当操作不一致的时候,就无法解决问题,会出现下面的结果
public class Bank {
private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
// TODO Auto-generated method stub
return 0;
}
};
// 存钱
public void addMoney(int money) {
count.set(count.get()+money);
System.out.println(System.currentTimeMillis() + "存进:" + money);
}
// 取钱
public void subMoney(int money) {
if (count.get() - money < 0) {
System.out.println("余额不足");
return;
}
count.set(count.get()- money);
System.out.println(+System.currentTimeMillis() + "取出:" + money);
}
// 查询
public void lookMoney() {
System.out.println("账户余额:" + count.get());
}
}
package threadTest;
public class SyncThreadTest {
public static void main(String args[]){
final Bank bank=new Bank();
Thread tadd=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bank.addMoney(100);
bank.lookMoney();
System.out.println("\n");
}
}
});
Thread tsub = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
bank.subMoney(100);
bank.lookMoney();
System.out.println("\n");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
tsub.start();
tadd.start();
}
}
结果:
余额不足
账户余额:0
余额不足
1502547748383存进:100
账户余额:100
账户余额:0
余额不足
账户余额:0
1502547749383存进:100
账户余额:200