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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

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

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

Avatar for Dmitry Vyazelenko

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