标签:getname 对象 java font ring unlock static money 利用
1. 怎样让多线程下的类安全起来
无状态、加锁、让类不可变、栈封闭、安全的发布对象
2. 死锁
2.1 死锁概念及解决死锁的原则
一定发生在多个线程争夺多个资源里的情况下,发生的原因是每个线程拿到了某个(某些)资源不释放,同时等待着其他线程所持有的资源。
解决死锁的原则就是确保正确的获取资源的顺序,或者获取资源时使用定时尝试机制。
2.2 常见的死锁:
简单顺序死锁:
package com.study.deadlock.bank; /** * 简单顺序死锁 * 解决办法:保证拿锁的顺序一致 * @author THINKPAD * */ public class SimpleDeadLock { //左锁 private static Object left = new Object(); //右锁 private static Object right = new Object(); private static void leftToRight() throws InterruptedException { synchronized (left){ System.out.println(Thread.currentThread().getName()+" get left"); Thread.sleep(100); synchronized (right){ System.out.println(Thread.currentThread().getName()+" get right"); } } } private static void rightToLeft() throws InterruptedException { synchronized (left){ System.out.println(Thread.currentThread().getName()+" get right-left"); Thread.sleep(100); synchronized (right){ System.out.println(Thread.currentThread().getName()+" get left-right"); } } } private static class TestThread extends Thread{ private String name; public TestThread(String name) { this.name = name; } @Override public void run(){ try { rightToLeft(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Thread.currentThread().setName("Main"); TestThread testThread = new TestThread("testThread"); testThread.start(); try { leftToRight(); } catch (InterruptedException e) { e.printStackTrace(); } } }
动态顺序死锁:
A. 通过特殊手段保证拿锁的顺序一致
package com.study.deadlock.bank.serivice; import com.study.deadlock.bank.Account; /** * 动态顺序死锁 * @author THINKPAD * 解决办法:通过特殊手段保证拿锁的顺序一致,如获取要锁定对象的hash值,然后比较大小,先锁小的再锁大的 */ public class SafeTransfer implements ITransfer { private static Object tieLock = new Object(); @Override public void transfer(Account from, Account to, int amount) throws InterruptedException { //获取要锁定对象的hash值,然后比较大小,先锁小的再锁大的 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{ //hash值相等时在前面再加一把锁 synchronized (tieLock){ synchronized (to){ System.out.println(Thread.currentThread().getName()+" get "+from.getName()); Thread.sleep(100); synchronized (from){ System.out.println(Thread.currentThread().getName() +" get "+to.getName()); from.flyMoney(amount); to.addMoney(amount); } } } } } }
B. 定时轮询获取锁即定时尝试获取锁
package com.study.deadlock.bank; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 账户信息 * @author THINKPAD * */ public class Account { private long number; private final String name; private int money; private final Lock lock = new ReentrantLock(); public Lock getLock() { return lock; } public Account(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 "Account{" + "name=‘" + name + ‘\‘‘ + ", money=" + money + ‘}‘; } public void addMoney(int amount){ money = money + amount; } public void flyMoney(int amount){ money = money - amount; } }
package com.study.deadlock.bank.serivice; import com.study.deadlock.bank.Account; import java.util.Random; /** * 动态顺序死锁 * @author THINKPAD * * 解决办法: * 定时轮询获取锁即定时尝试获取锁 */ public class TryLockTransfer implements ITransfer { @Override public void transfer(Account from, Account to, int amount) throws InterruptedException { Random r = new Random(); while(true){ if(from.getLock().tryLock()){ try{ System.out.println(Thread.currentThread().getName() +" get from "+from.getName()); if(to.getLock().tryLock()){ try{ System.out.println(Thread.currentThread().getName() +" get to "+to.getName()); from.flyMoney(amount); to.addMoney(amount); System.out.println(from); System.out.println(to); break; }finally { to.getLock().unlock(); } } }finally { from.getLock().unlock(); } } Thread.sleep(r.nextInt(5));//防止产生活锁 } } }
3. 活锁
概念:多个线程同时获取锁,当去拿另外的一把锁时发现被其他线程持有,觉得其他线程可能需要自己手中的锁就释放自己持有的锁,这样不断的循环就产生了活锁
4. 对性能的思考
4.1、 程序的安全性优于性能的提升
4.2、 使用多线程会带来额外的性能开销,滥用线程,有可能导致得不偿失。
4.3、 所谓性能,包含多个指标。例如“多快”:服务时间、等待时间、延迟时间;例如“多少”:吞吐量,例如可伸缩性等等。
4.4、 性能的各个指标方面,是完全独立的,有时候甚至是相互矛盾。
4.5、 所以性能的提升是个包括成本在内多方面权衡和妥协的结果。
性能优化的黄金原则:
首先保证程序正确,然后再提高运行速度(如果有确切的证据表明程序确实慢)。
5. Amdahl定律
F :程序中的串行部分,是个百分比(100%-1%),
N:cpu的个数
Speedup:指在增加cpu的情况下,程序的加速比
注意:任何程序都会有串行部分
6. 线程引入的开销
上下文的切换
内存同步
阻塞
7. 减少锁的竞争
快进快出,缩小锁的范围,将与锁无关的,有大量计算或者阻塞操作的代码移出同步范围。
减小锁的粒度,多个相互独立的状态变量可以使用多个锁来保护,每个锁只保护一个变量。
锁的分段,例如ConcurrentHashMap中的实现。
减少独占锁的使用,例如读多写少的情况下,用读写锁替换排他锁。
8. 安全的单例模式
懒汉式单例
package com.study.dcl; /** * 懒汉式单例-双重检查 * @author THINKPAD * */ public class SingleDcl { //双重检查不能保证线程安全,原因是第一个线程可能还没有初始化完, //第二个线程就进来获取单例对象使用了,所以加一个volatile修饰保证可见性 private volatile static SingleDcl single; private SingleDcl(){} public static SingleDcl getInstance(){ if(null==single){ synchronized (SingleDcl.class){ if(single==null){ single = new SingleDcl(); } } } return single; } }
饿汉式单例
package com.study.dcl; /** * 饿汉式单例-线程安全 * @author THINKPAD * */ public class SingleEHan { public static SingleEHan singleEHan = new SingleEHan(); private SingleEHan(){}; }
延迟类占位符单例
package com.study.dcl; /** * 延迟类占位符单例,利用JVM的类加载的时候会自动给加载的类加上锁的机制 * @author THINKPAD * */ public class SingleClassInit { private SingleClassInit(){} private static class InstanceHolder{ public static SingleClassInit instance = new SingleClassInit(); } public static SingleClassInit getInstance(){ return InstanceHolder.instance; } }
总结:单例建议使用延迟类占位符单例和枚举类型的单例
标签:getname 对象 java font ring unlock static money 利用
原文地址:https://www.cnblogs.com/leeSmall/p/8976004.html