Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Многопоточное программирование это СЛОЖНО!

Многопоточное программирование это СЛОЖНО!

Писать корректные однопоточные приложения сложно. Писать корректные многопоточные приложения на несколько порядков сложнее. Потому что, все, что может поломаться в однопоточном приложении, может также сломаться и в многопоточном, но в добавок к этому много других подводных камней скрываются под потоками. В этом докладе я хочу рассказать о том, что делает многопоточное программирование таким сложным: начиная с хардвара и двигаясь по всему софтверному стэку (ОС, рантайм, приложение). Поговорим про то, какие фундаментальные вещи полезно знать и о чем стоит помнить прежде чем нырять с головой в многопоточность. А еще мы рассмотрим какие альтернативные модели многопоточности существуют и как они могут упростить нам жизнь.

Dmitry Vyazelenko

October 18, 2015
Tweet

More Decks by Dmitry Vyazelenko

Other Decks in Programming

Transcript

  1. 2 About me • Senior Software Engineer @ Sowatec AG,

    Switzerland • Disorganizer at JCrete Unconference, www.jcrete.org • Contacts: vyazelenko.com, @DVyazelenko
  2. 3 Содержание • Ошибаются все • Халява кончилась • Многопоточное

    программирование это СЛОЖНО • И как нам с этим жить
  3. 5

  4. 7

  5. 9

  6. HotSpot optimization bug 10 public void think() { while (true)

    { if (checkInterruptedStatus()) break; } System.out.println("We're done thinking"); } private boolean checkInterruptedStatus() { return Thread.currentThread().isInterrupted(); } http://cs.oswego.edu/pipermail/concurrency-interest/2012-November/010184.html http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8003135
  7. 18 public class Account { private long balance; public Account(long

    money) { this.balance = money; } public long balance() { return balance; } public void debit(long amount) { balance -= amount; } public void credit(long amount) { balance += amount; } }
  8. 19 public class Account { private final Lock lock =

    new ReentrantLock(); private long balance; public Account(long amount) { this.balance = amount; } public long balance() { lock.lock(); try { return balance; } finally { lock.unlock(); } } public void debit(long amount) { lock.lock(); try { balance -= amount; } finally { lock.unlock(); } } public void credit(long amount) { lock.lock(); try { balance += amount; } finally { lock.unlock(); } } }
  9. 20 public class AccountManager { public void transfer(Account src, Account

    dest, long amount) { if (src.balance() < amount) { throw new IllegalArgumentException("Insufficient funds!"); } src.debit(amount); dest.credit(amount); } }
  10. 20 public class AccountManager { public void transfer(Account src, Account

    dest, long amount) { if (src.balance() < amount) { throw new IllegalArgumentException("Insufficient funds!"); } src.debit(amount); dest.credit(amount); } }
  11. 21 public class Account { private final Lock lock =

    new ReentrantLock(); private long balance; public Account(long amount) { this.balance = amount; } public long balance() { lock.lock(); try { return balance; } finally { lock.unlock(); } } public void debit(long amount) { ... } public void credit(long amount) { ... } Lock getLock() { return lock; } }
  12. 22 public class AccountManager { public void transfer(Account src, Account

    dest, long amount) { Lock srcLock = src.getLock(); srcLock.lock(); try { Lock destLock = dest.getLock(); destLock.lock(); try { if (src.balance() < amount) { throw new IllegalArgumentException("Insufficient funds!"); } src.debit(amount); dest.credit(amount); } finally { destLock.unlock(); } } finally { srcLock.unlock(); } } }
  13. 23 public class TransferDeadlock { public static void main(String[] args)

    throws Exception { AccountManager manager = new AccountManager(); Account src = new Account(1000); Account dest = new Account(1000); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { manager.transfer(src, dest, 1); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { manager.transfer(dest, src, 1); } }); t1.start(); t2.start(); t1.join(); t2.join(); } }
  14. 24 Found one Java-level deadlock: ============================= "Thread-1": waiting for ownable

    synchronizer 0x000000076abcced0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "Thread-0" "Thread-0": waiting for ownable synchronizer 0x000000076abccf18, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076abcced0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.vyazelenko.AccountManager.transfer(AccountManager.java:11) at com.vyazelenko.TransferDeadlock.lambda$main$1(TransferDeadlock.java:16) at com.vyazelenko.TransferDeadlock$$Lambda$2/363771819.run(Unknown Source) at java.lang.Thread.run(Thread.java:745) "Thread-0": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076abccf18> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.vyazelenko.AccountManager.transfer(AccountManager.java:11) at com.vyazelenko.TransferDeadlock.lambda$main$0(TransferDeadlock.java:11) at com.vyazelenko.TransferDeadlock$$Lambda$1/668386784.run(Unknown Source) at java.lang.Thread.run(Thread.java:745) Found 1 deadlock.
  15. 25

  16. 26 public class Account { private final Lock lock =

    new ReentrantLock(); private final int accountNo; private long balance; public Account(long money, int accountNo) { this.balance = money; this.accountNo = accountNo; } public long balance() { ... } public void debit(long money) { ... } public void credit(long money) { ... } Lock getLock() { return lock; } public int getAccountNo() { return accountNo; } }
  17. 27 public class AccountManager { public void transfer(Account src, Account

    dest, long amount) { Lock srcLock = src.getLock(); Lock destLock = dest.getLock(); if (src.getAccountNo() > dest.getAccountNo()) { srcLock = dest.getLock(); destLock = src.getLock(); } srcLock.lock(); try { destLock.lock(); try { if (src.balance() < amount) { throw new IllegalArgumentException("Insufficient funds!"); } src.debit(amount); dest.credit(amount); } finally { destLock.unlock(); } } finally { srcLock.unlock(); } } }
  18. 30

  19. Message passing concurrency 35 • Communicating sequential processes (Go) •

    Actor Model (Akka, Erlang) Don't communicate by sharing memory; share memory by communicating.
  20. Ссылки • The Free Lunch Is Over: A Fundamental Turn

    Toward Concurrency in Software (http://www.gotw.ca/publications/concurrency-ddj.htm) • Amdahl's law (https://en.wikipedia.org/wiki/Amdahl%27s_law) • How to Quantify Scalability (http://www.perfdynamics.com/Manifesto/ USLscalability.html) • Java Concurrency in Practice (http://www.amazon.com/Java-Concurrency- Practice-Brian-Goetz/dp/0321349601/ref=sr_1_1? s=books&ie=UTF8&qid=1445075658&sr=1-1&keywords=Java+Concurrency +in+Practice) • The Art of Multiprocessor Programming (http://www.amazon.com/Art- Multiprocessor-Programming-Revised-Reprint/dp/0123973376/ref=sr_1_5? s=books&ie=UTF8&qid=1445075883&sr=1-5&keywords=concurrent +programming) 38
  21. Ссылки • Java Concurrent Animated (http://www.jconcurrent.com/) • Scalability! But at

    what COST? (http:// www.frankmcsherry.org/graph/scalability/cost/2015/01/15/ COST.html) • http://mechanical-sympathy.blogspot.ru/ • https://groups.google.com/forum/#!forum/mechanical- sympathy • Concurrency-interest mailing list (http:// altair.cs.oswego.edu/mailman/listinfo/concurrency-interest) 39