多线程使用(线程安全)
一、死锁演示与解决
1、锁有简单锁,和动态锁,简单锁保证锁的顺序即可,动态锁需要根据某个标准获取锁
2、普通死锁演示
public class NormalDeadLock {
private static Object valueFirst = new Object();//第一个锁
private static Object valueSecond = new Object();//第二个锁
//先拿第一个锁,再拿第二个锁
private static void fisrtToSecond() throws InterruptedException {
String threadName = Thread.currentThread().getName();
synchronized (valueFirst){
System.out.println(threadName+" get valueSecond");
Thread.sleep(100);
synchronized (valueSecond){
System.out.println(threadName+" get valueFirst");
}
}
}
//先拿第二个锁,再拿第一个锁
private static void SecondToFisrt() throws InterruptedException {
String threadName = Thread.currentThread().getName();
synchronized (valueSecond){
System.out.println(threadName+" get valueSecond");
Thread.sleep(100);
synchronized (valueFirst){
System.out.println(threadName+" get valueFirst");
}
}
}
private static class TestThread extends Thread{
private String name;
public TestThread(String name) {
this.name = name;
}
public void run(){
Thread.currentThread().setName(name);
try {
SecondToFisrt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread.currentThread().setName("TestDeadLock");
TestThread testThread = new TestThread("SubTestThread");
testThread.start();
try {
fisrtToSecond();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3、动态锁演示,参数传入顺序不同,锁的顺序也不同,System.identityHashCode(from);不管参数的传入顺序,总是先锁值小的
public class UserAccount {
///private int id;
private final String name;//账户名称
private int money;//账户余额
private final Lock lock = new ReentrantLock();
public Lock getLock() {
return lock;
}
public UserAccount(String name, int amount) {
this.name = name;
this.money = amount;
}
public String getName() {
return name;
}
public int getAmount() {
return money;
}
@Override
public String toString() {
return "UserAccount{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
//转入资金
public void addMoney(int amount){
money = money + amount;
}
//转出资金
public void flyMoney(int amount){
money = money - amount;
}
}
public interface ITransfer {
void transfer(UserAccount from, UserAccount to, int amount)
throws InterruptedException;
}
public class SafeOperate implements ITransfer {
private static Object tieLock = new Object();
@Override
public void transfer(UserAccount from, UserAccount to, int amount)
throws InterruptedException {
int fromHash = System.identityHashCode(from);
int toHash = System.identityHashCode(to);
if(fromHash<toHash){
synchronized (from){
System.out.println(Thread.currentThread().getName()+" get "+from.getName());
Thread.sleep(100);
synchronized (to){
System.out.println(Thread.currentThread().getName()+" get "+to.getName());
from.flyMoney(amount);
to.addMoney(amount);
System.out.println(from);
System.out.println(to);
}
}
}else if(toHash<fromHash){
synchronized (to){
System.out.println(Thread.currentThread().getName()+" get"+to.getName());
Thread.sleep(100);
synchronized (from){
System.out.println(Thread.currentThread().getName()+" get"+from.getName());
from.flyMoney(amount);
to.addMoney(amount);
System.out.println(from);
System.out.println(to);
}
}
}else{
synchronized (tieLock){
synchronized (from){
synchronized (to){
from.flyMoney(amount);
to.addMoney(amount);
}
}
}
}
}
}
private static class TransferThread extends Thread{
private String name;
private UserAccount from;
private UserAccount to;
private int amount;
private ITransfer transfer;
public TransferThread(String name, UserAccount from, UserAccount to,
int amount, ITransfer transfer) {
this.name = name;
this.from = from;
this.to = to;
this.amount = amount;
this.transfer = transfer;
}
public void run(){
Thread.currentThread().setName(name);
try {
transfer.transfer(from,to,amount);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
PayCompany payCompany = new PayCompany();
UserAccount zhangsan = new UserAccount("zhangsan",20000);
UserAccount lisi = new UserAccount("lisi",20000);
ITransfer transfer = new SafeOperateToo();
TransferThread zhangsanToLisi = new TransferThread("zhangsanToLisi"
,zhangsan,lisi,2000,transfer);
TransferThread lisiToZhangsan = new TransferThread("lisiToZhangsan"
,lisi,zhangsan,4000,transfer);
zhangsanToLisi.start();
lisiToZhangsan.start();
}
}
二、CAS演示与问题
1、cas在计算机科学中,比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指令。
比较并且交换: 三个运算符: 一个内存地址V,一个期望的旧值A,一个新值B,操作的时候如果这个地址上存放的值等于这个期望的值A,则将地址上的值赋为新值B,否则不做任何操作。
public class UseAtomicReference {
public static AtomicReference<UserInfo> atomicUserRef = new
AtomicReference<UserInfo>();//原子引用类型的实例
public static void main(String[] args) {
UserInfo user = new UserInfo("Mark", 15);//要修改的实体的实例
atomicUserRef.set(user);//用原子引用类型包装
UserInfo updateUser = new UserInfo("Bill", 17);//要变化成为的新实例
atomicUserRef.compareAndSet(user, updateUser);
System.out.println(atomicUserRef.get().getName());
System.out.println(atomicUserRef.get().getAge());
//System.out.println(user.getName());
//System.out.println(user.getAge());
}
//定义一个实体类
static class UserInfo {
private String name;
private int age;
public UserInfo(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
}
2、CAS中的ABA问题与解决
在CAS算法中,需要取出内存中某时刻的数据(由用户完成),在下一时刻比较并替换(由CPU完成,该操作是原子的)。这个时间差中,会导致数据的变化。
使用版本戳解决:AtomicStampedReference
状态戳可类比为时间戳,是一个整数值,每一次修改对象值的同时,也要修改状态戳,从而区分相同对象值的不同状态。当AtomicStampedReference设置对象值时,对象值以及状态戳都必须满足期望值,写入才会成功。
public class UseAtomicStampedReference {
static AtomicStampedReference<String> asr =
new AtomicStampedReference("mark", 0);
public static void main(String[] args) throws InterruptedException {
final int oldStamp = asr.getStamp();//拿初始版本0
final String oldReference = asr.getReference();//初始值
System.out.println(oldReference+"============"+oldStamp);
Thread rightStampThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":当前变量值:"
+oldReference + "-当前版本戳:" + oldStamp + "-"
+ asr.compareAndSet(oldReference,
oldReference + "+Java", oldStamp, oldStamp + 1));
}
});
Thread errorStampThread = new Thread(new Runnable() {
@Override
public void run() {
String reference = asr.getReference();//变量的最新值
System.out.println(Thread.currentThread().getName()+":当前变量值:"
+reference + "-当前版本戳:" + asr.getStamp() + "-"
+ asr.compareAndSet(reference,
reference + "+C", oldStamp, oldStamp + 1));
}
});
rightStampThread.start();
rightStampThread.join();
errorStampThread.start();
errorStampThread.join();
System.out.println(asr.getReference()+"============"+asr.getStamp());
}
}