1/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Vláknové programování část IX Lukáš Hejmánek, Petr Holub {xhejtman,hopet}@ics.muni.cz Laboratoř pokročilých síťových technologií PV192 2010­03­23 2/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Přehled přednášky ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy (příklady povětšinou převzaty z JCiP, Goetz) 3/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy ThreadFactory TPE vytváří vlákna pomocí ThreadFactory default ThreadFactory: nedémonická, bez speciálních nastavení Možnost přede novat, jak se budou vytvářet vlákna nastavení pojmenování vláken vlastní třída vytvářených vláken (statistiky, ladění) speci kace vlastního UncaughtExceptionHandler nastavení priorit (raději nedělat) nastavení démonického stavu (raději nedělat) v případě použití bezpečnostních politik (security policies) lze použít privilegedThreadFactory podědění oprávnění, AccessControlContext a contextClassLoader od vlákna vytvářejícího privilegedThreadFactory 4/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy ThreadFactory public class MyThreadFactory implements ThreadFactory { 2 private final String poolName; class MyAppThread extends Thread { 4 public MyAppThread(Runnable runnable, String poolName) { super(runnable, poolName); 6 } } 8 public MyThreadFactory(String poolName) { 10 this.poolName = poolName; } 12 public Thread newThread(Runnable runnable) { 14 return new MyAppThread(runnable, poolName); } 16 } 5/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Modi kace Executorů za běhu settery a gettery na různé vlastnosti možnost přetypování executorů vyrobených přes factory metody (kromě newSingleThreadExecutor) na ThreadPoolExecutor omezení modi kací nechceme nechat vývojáře šťourat do svých TPE factory metoda Executor.unconfigurableExecutorService bere ExecutorService vrací omezenou ExecutorService pomocí DelegatedExecutorService, která rozšiřuje AbstractExecutorService využívání metodou newSingleThreadExecutor 6/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Modi kace TPE Háčky pro modi kace beforeExecute afterExecute terminate Např. sběr statistik 7/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Modi kace TPE public class TimingThreadPool extends ThreadPoolExecutor { 2 public TimingThreadPool() { 4 super(1, 1, 0L, TimeUnit.SECONDS, null); } 6 private final ThreadLocal startTime = new ThreadLocal(); 8 private final Logger log = Logger.getLogger("TimingThreadPool"); private final AtomicLong numTasks = new AtomicLong(); 10 private final AtomicLong totalTime = new AtomicLong(); 12 protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); 14 log.fine(String.format("Thread %s: start %s", t, r)); startTime.set(System.nanoTime()); 16 } 8/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Modi kace TPE protected void afterExecute(Runnable r, Throwable t) { 2 try { long endTime = System.nanoTime(); 4 long taskTime = endTime - startTime.get(); numTasks.incrementAndGet(); 6 totalTime.addAndGet(taskTime); log.fine(String.format("Thread %s: end %s, time=%dns", 8 t, r, taskTime)); } finally { 10 super.afterExecute(r, t); } 12 } 14 protected void terminated() { try { 16 log.info(String.format("Terminated: avg time=%dns", totalTime.get() / numTasks.get())); 18 } finally { super.terminated(); 20 } } 22 } 9/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Možné řešení kvízu public class DynamicTPE { 2 static class CustomTPE extends ThreadPoolExecutor { final int userProvidedPoolSize; 4 final int queueCapacity; 6 CustomTPE(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 8 BlockingQueue workQueue) { super(corePoolSize, maximumPoolSize, 10 keepAliveTime, unit, workQueue); userProvidedPoolSize = corePoolSize; 12 queueCapacity = workQueue.size() + workQueue.remainingCapacity(); 14 } 16 @Override public Future submit(Runnable task) { autoAdjustCorePoolSize(); 18 return super.submit(task); } 20 @Override synchronized public void setCorePoolSize 22 (int corePoolSize) { super.setCorePoolSize(corePoolSize); 24 } 26 @Override synchronized public void setMaximumPoolSize (int maximumPoolSize) { 28 super.setMaximumPoolSize(maximumPoolSize); 10/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Možné řešení kvízu 1 synchronized private void autoAdjustCorePoolSize() { final int queueRemaining = getQueue().remainingCapacity(); 3 final int extension; if (queueRemaining < 0.25 * queueCapacity) { 5 extension = (int) Math.round(0.25 * queueCapacity - queueRemaining); 7 if (getCorePoolSize() + extension < getMaximumPoolSize()) setCorePoolSize(getCorePoolSize() + extension); 9 else setCorePoolSize(getMaximumPoolSize()); 11 } else if (queueRemaining > 0.75 * queueCapacity) { 13 extension = (int) Math.round(queueRemaining - 0.75 * queueCapacity); 15 if (getCorePoolSize() - extension > userProvidedPoolSize) setCorePoolSize(getCorePoolSize() - extension); 17 else setCorePoolSize(userProvidedPoolSize); 19 } } 21 } 11/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Možné řešení kvízu public static void main(String[] args) { 2 ThreadPoolExecutor tpe = new CustomTPE(1, 10, 60, TimeUnit.SECONDS, new tpe.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy( 4 ArrayList taskList = new ArrayList(); Runnable r = new Runnable() { 6 public void run() { try { 8 Thread.sleep(30); } catch (InterruptedException e) { 10 } System.out.println("bla"); 12 } }; 14 for (int i = 0; i < 1000; i++) { Future f = tpe.submit(r); 16 taskList.add(f); System.out.println("TPE corepoolsize: " + tpe.getCorePoolSize()); 18 System.out.println("TPE poolsize: " + tpe.getPoolSize()); } 20 for (Future future : taskList) { try { 22 Object o = future.get(); } catch (InterruptedException e) { 24 } catch (ExecutionException e) { } 26 } tpe.shutdown(); 28 } 12/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Možné řešení kvízu 1 TPE corepoolsize: 1 TPE poolsize: 1 3 TPE corepoolsize: 1 TPE poolsize: 1 5 TPE corepoolsize: 1 TPE poolsize: 1 7 TPE corepoolsize: 1 TPE poolsize: 1 9 TPE corepoolsize: 1 TPE poolsize: 1 11 TPE corepoolsize: 1 TPE poolsize: 1 13 TPE corepoolsize: 1 TPE poolsize: 1 15 TPE corepoolsize: 1 TPE poolsize: 1 17 TPE corepoolsize: 1 TPE poolsize: 1 19 TPE corepoolsize: 1 TPE poolsize: 1 21 TPE corepoolsize: 1 TPE poolsize: 1 23 TPE corepoolsize: 1 TPE poolsize: 1 2 TPE corepoolsize: 1 TPE poolsize: 1 4 TPE corepoolsize: 1 TPE poolsize: 1 6 TPE corepoolsize: 1 TPE poolsize: 1 8 TPE corepoolsize: 1 TPE poolsize: 1 10 TPE corepoolsize: 1 TPE poolsize: 1 12 TPE corepoolsize: 2 TPE poolsize: 2 14 TPE corepoolsize: 4 TPE poolsize: 4 16 TPE corepoolsize: 7 TPE poolsize: 7 18 TPE corepoolsize: 10 TPE poolsize: 10 20 TPE corepoolsize: 10 TPE poolsize: 10 22 bla bla 13/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Možné řešení kvízu Stojí nám to za to? drahá synchronizace režije se startováním a ukončováním vláken nestačí vhodně nastavit corePoolSize a allowCoreThreadTimeOut? 14/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Kompletně vlastní implementace TPE Zdrojové kódy: http://kickjava.com/src/java/util/concurrent/ ThreadPoolExecutor.java.htm http://kickjava.com/src/java/util/concurrent/ ScheduledThreadPoolExecutor.java.htm 15/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Deadlock Deadlock ­ uváznutí, smrtelné objetí ;-) Vzájemné nekončící čekání na zámky Potřeba globálního uspořádání zámků zamykání podle globálního uspořádání Možnost využití Lock.tryLock() náhodný rovnoměrný back-o náhodný exponenciální back-o nelze použít s monitory Řešení deadlocků runtimem (ne v Javě) 16/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Deadlock public static void transferMoney(Account fromAccount, 2 Account toAccount, DollarAmount amount) 4 throws InsufficientFundsException { synchronized (fromAccount) { 6 synchronized (toAccount) { if (fromAccount.getBalance().compareTo(amount) < 0) 8 throw new InsufficientFundsException(); else { 10 fromAccount.debit(amount); toAccount.credit(amount); 12 } } 14 } } 17/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Deadlock public static void transferMoney(Account fromAccount, 2 Account toAccount, DollarAmount amount) 4 throws InsufficientFundsException { synchronized (fromAccount) { 6 synchronized (toAccount) { if (fromAccount.getBalance().compareTo(amount) < 0) 8 throw new InsufficientFundsException(); else { 10 fromAccount.debit(amount); toAccount.credit(amount); 12 } } 14 } } 18/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Deadlock public void transferMoney(final Account fromAcct, 2 final Account toAcct, final DollarAmount amount) 4 throws InsufficientFundsException { class Helper { 6 public void transfer() throws InsufficientFundsException { if (fromAcct.getBalance().compareTo(amount) < 0) 8 throw new InsufficientFundsException(); else { 10 fromAcct.debit(amount); toAcct.credit(amount); 12 } } 14 } int fromHash = System.identityHashCode(fromAcct); 16 int toHash = System.identityHashCode(toAcct); System.identityHashCode(o) může vrátit pro dva různé objekty identický hash řídký problém 19/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Deadlock if (fromHash < toHash) { 2 synchronized (fromAcct) { synchronized (toAcct) { 4 new Helper().transfer(); } 6 } } else if (fromHash > toHash) { 8 synchronized (toAcct) { synchronized (fromAcct) { 10 new Helper().transfer(); } 12 } } else { 14 synchronized (tieLock) { synchronized (fromAcct) { 16 synchronized (toAcct) { new Helper().transfer(); 18 } } 20 } } 20/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Deadlock private static Random rnd = new Random(); 2 public boolean transferMoney(Account fromAcct, 4 Account toAcct, DollarAmount amount, 6 long timeout, TimeUnit unit) 8 throws InsufficientFundsException, InterruptedException { long fixedDelay = getFixedDelayComponentNanos(timeout, unit); 10 long randMod = getRandomDelayModulusNanos(timeout, unit); long stopTime = System.nanoTime() + unit.toNanos(timeout); 21/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Deadlock while (true) { 2 if (fromAcct.lock.tryLock()) { try { 4 if (toAcct.lock.tryLock()) { try { 6 if (fromAcct.getBalance().compareTo(amount) < 0) throw new InsufficientFundsException(); 8 else { fromAcct.debit(amount); 10 toAcct.credit(amount); return true; 12 } } finally { 14 toAcct.lock.unlock(); } 16 } } finally { 18 fromAcct.lock.unlock(); } 20 } if (System.nanoTime() < stopTime) 22 return false; NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod); 24 } 22/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Otevřená volání 1 class Taxi { @GuardedBy("this") private Point location, destination; 3 private final Dispatcher dispatcher; 5 public Taxi(Dispatcher dispatcher) { this.dispatcher = dispatcher; 7 } 9 public synchronized Point getLocation() { return location; 11 } 13 public synchronized void setLocation(Point location) { this.location = location; 15 if (location.equals(destination)) dispatcher.notifyAvailable(this); 17 } 19 public synchronized Point getDestination() { return destination; 21 } 23 public synchronized void setDestination(Point destination) { this.destination = destination; 25 } } 23/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Otevřená volání class Dispatcher { 2 @GuardedBy("this") private final Set taxis; @GuardedBy("this") private final Set availableTaxis; 4 public Dispatcher() { 6 taxis = new HashSet(); availableTaxis = new HashSet(); 8 } 10 public synchronized void notifyAvailable(Taxi taxi) { availableTaxis.add(taxi); 12 } 14 public synchronized Image getImage() { Image image = new Image(); 16 for (Taxi t : taxis) image.drawMarker(t.getLocation()); 18 return image; } 20 } 24/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Otevřená volání Otevřené volání (open call) volání cizí, které není obaleno žádnou synchronizací preferovaný způsob Převod na otevřené volání synchronizace by měla být omezena na lokální proměnné problém se zachováním sémantiky Možnost globálního zámku 25/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Otevřená volání 1 class Taxi { @GuardedBy("this") private Point location, destination; 3 private final Dispatcher dispatcher; 5 public Taxi(Dispatcher dispatcher) { this.dispatcher = dispatcher; } 7 public synchronized Point getLocation() { return location; } 9 public void setLocation(Point location) { boolean reachedDestination; 11 synchronized (this) { this.location = location; 13 reachedDestination = location.equals(destination); } 15 if (reachedDestination) dispatcher.notifyAvailable(this); 17 } 19 public synchronized Point getDestination() { return destination; } 21 public synchronized void setDestination(Point destination) { this.destination = destination; 23 } } 26/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Otevřená volání class Dispatcher { 2 @GuardedBy("this") private final Set taxis; @GuardedBy("this") private final Set availableTaxis; 4 public Dispatcher() { 6 taxis = new HashSet(); availableTaxis = new HashSet(); 8 } 10 public synchronized void notifyAvailable(Taxi taxi) { availableTaxis.add(taxi); 12 } 14 public Image getImage() { Set copy; 16 synchronized (this) { copy = new HashSet(taxis); 18 } Image image = new Image(); 20 for (Taxi t : copy) image.drawMarker(t.getLocation()); 22 return image; } 24 } 27/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Hladovění Hladovění (starvation) nastává, pokud je vláknu neustále odpírán zdroj, který je potřeba k dalšímu postupu běžné použití zámků je férové problém při nastavování priorit t.setPriority(Thread.MIN_PRIORITY); // 1 2 t.setPriority(Thread.NORM_PRIORITY); // 5 t.setPriority(Thread.MAX_PRIORITY); // 10 problém platformové závislosti priorit možná pomoc pro zvýšení responsivity GUI typické pokusy o ,,řešení`` problémů 1 Thread.yield(); Thread.sleep(100); 28/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Další typy uváznutí Livelock uváznutí, při němž se vlákno (aktivně) snaží o činnosti, která opakovaně selhává náhodnostní exponenciální back-o Ztracené právy o.wait() a o.notify() resp. o.notifyAll nemají mechanismus zdržení noti kace pokud vlákno usne na o.wait() později, než mělo být noti kováno přes o.notify, nikdy se nevzbudí 29/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Další typy uváznutí Podmíněná signalizace 30/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Další typy uváznutí // podmineny predikat musi byt chraneny zamkem 2 synchronized (lock) { while (!conditionPredicate) 4 lock.wait(); // nyni je objekt v pozadovanem stavu 6 } Pravidla pro signalizaci s podmínkami 1. zformulovat a ověřit podmínku před voláním wait() 2. wait() bežet ve smyčce, kontrolovat po vzbuzení probuzení z wait() mohlo nastat z jiného důvodu 3. zajistit, aby proměnné v podmínce byly chráněny tím zámkem, který se používá v monitoru 4. držet zámek v době volání wait(), notify(), notifyAll() Potřeba zajistit, aby při změně podmínky vždy někdo zasignalizoval Signál se může ztratit, pokud bychom se vzdali mezi dalším testem monitoru 31/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Hledání problémů Výpis stavu JVM SIGQUIT na unixech (ev. Ctrl- pokud mapuje na SIGQUIT) Ctrl-Break na Windows 32/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Hledání problémů public static void main(String[] args) { 2 final Object a = new Object(); final Object b = new Object(); 4 Thread t1 = new Thread(new Runnable() { 6 public void run() { try { 8 synchronized (a) { Thread.sleep(1000); 10 System.out.println("t1 - cekam na b"); synchronized (b) { 12 System.out.println("t1 - jsem zde"); } 14 } } catch (InterruptedException e) { 16 } } 18 }); 33/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Hledání problémů Thread t2 = new Thread(new Runnable() { 2 public void run() { try { 4 synchronized (b) { Thread.sleep(1000); 6 System.out.println("t2 - cekam na a"); synchronized (a) { 8 System.out.println("t2 - jsem zde"); } 10 } } catch (InterruptedException e) { 12 } } 14 }); 16 t1.start(); t2.start(); 34/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Hledání problémů $ java IntentionalDeadlock 2 t2 - cekam na a t1 - cekam na b 4 2010-04-22 11:46:25 Full thread dump Java HotSpot(TM) Client VM (16.2-b04 mixed mode, sharing): 6 "DestroyJavaVM" prio=6 tid=0x020b1000 nid=0x164c waiting on condition [0x00000000] 8 java.lang.Thread.State: RUNNABLE 10 "Thread-1" prio=6 tid=0x02149800 nid=0x1b4c waiting for monitor entry [0x0480f000] java.lang.Thread.State: BLOCKED (on object monitor) 12 at IntentionalDeadlock$2.run(IntentionalDeadlock.java:35) - waiting to lock <0x243e6928> (a java.lang.Object) 14 - locked <0x243e6930> (a java.lang.Object) at java.lang.Thread.run(Unknown Source) 16 "Thread-0" prio=6 tid=0x02146c00 nid=0x1a38 waiting for monitor entry [0x0477f000] 18 java.lang.Thread.State: BLOCKED (on object monitor) at IntentionalDeadlock$1.run(IntentionalDeadlock.java:20) 20 - waiting to lock <0x243e6930> (a java.lang.Object) - locked <0x243e6928> (a java.lang.Object) 22 at java.lang.Thread.run(Unknown Source) 35/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Hledání problémů 1 "Low Memory Detector" daemon prio=6 tid=0x02121400 nid=0xbd8 runnable [0x00000000] java.lang.Thread.State: RUNNABLE 3 "CompilerThread0" daemon prio=10 tid=0x02119800 nid=0x1708 waiting on condition [0x00000000] 5 java.lang.Thread.State: RUNNABLE 7 "Attach Listener" daemon prio=10 tid=0x02118400 nid=0x13d0 runnable [0x00000000] java.lang.Thread.State: RUNNABLE 9 "Signal Dispatcher" daemon prio=10 tid=0x02115400 nid=0x5a0 waiting on condition [0x00000000] 11 java.lang.Thread.State: RUNNABLE Heap 2 def new generation total 4928K, used 466K [0x243b0000, 0x24900000, 0x29900000) eden space 4416K, 10% used [0x243b0000, 0x24424828, 0x24800000) 4 from space 512K, 0% used [0x24800000, 0x24800000, 0x24880000) to space 512K, 0% used [0x24880000, 0x24880000, 0x24900000) 6 tenured generation total 10944K, used 0K [0x29900000, 0x2a3b0000, 0x343b0000) the space 10944K, 0% used [0x29900000, 0x29900000, 0x29900200, 0x2a3b0000) 8 compacting perm gen total 12288K, used 42K [0x343b0000, 0x34fb0000, 0x383b0000) the space 12288K, 0% used [0x343b0000, 0x343ba960, 0x343baa00, 0x34fb0000) 10 ro space 10240K, 51% used [0x383b0000, 0x388dae00, 0x388dae00, 0x38db0000) rw space 12288K, 54% used [0x38db0000, 0x394472d8, 0x39447400, 0x399b0000) 36/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Hledání problémů Found one Java-level deadlock: 2 ============================= "Thread-1": 4 waiting to lock monitor 0x020d53ac (object 0x243e6928, a java.lang.Object), which is held by "Thread-0" 6 "Thread-0": waiting to lock monitor 0x020d6c74 (object 0x243e6930, a java.lang.Object), 8 which is held by "Thread-1" 10 Java stack information for the threads listed above: =================================================== 12 "Thread-1": at IntentionalDeadlock$2.run(IntentionalDeadlock.java:35) 14 - waiting to lock <0x243e6928> (a java.lang.Object) - locked <0x243e6930> (a java.lang.Object) 16 at java.lang.Thread.run(Unknown Source) "Thread-0": 18 at IntentionalDeadlock$1.run(IntentionalDeadlock.java:20) - waiting to lock <0x243e6930> (a java.lang.Object) 20 - locked <0x243e6928> (a java.lang.Object) at java.lang.Thread.run(Unknown Source) 22 Found 1 deadlock. 37/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Statická analýza kódu FindBugs http://findbugs.sourceforge.net/ 38/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Anotace Vícečlenný tým programátorů ­ předávání myšlenek komentáře v kódy anotace anotace se dají použít i pro statickou analýzu kódu import net.jcip.annotations.GuardedBy; 2 // http://www.javaconcurrencyinpractice.com/jcip-annotations.jar 39/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Anotace Anotace tříd @Immutable @ThreadSafe @NotThreadSafe Anotace polí @GuardedBy("this") monitor (intrinsic lock) na this @GuardedBy("jmenoPole") explicitní zámek na jmenoPole pokud je potomkem Lock jinak monitor na jmenoPole @GuardedBy("JmenoTridy.jmenoPole") obdobné, odkazuje se statické pole jiné třídy @GuardedBy("jmenoMetody()") metoda jmenoMetody() vrací zámek @GuardedBy("JmenoTridy.class") literál třídy (objekt) pro pojmenovanou třídu 40/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Omezování zámků zrychlení 1 s + 1-s n JVM se snaží dělat eliminaci synchronizací, které nemohou nastat (např. pomocí escape analysis) kombinace více zámků do jednoho (lock coarsening) Zbytečně nesynchronizovat delegace bezpečnosti (thread safety delegation) omezení rozsahu synchronizace (get in ­ get out principle, např. Taxi/Dispatcher) dělení zámků (lock splitting) ­ pouze pro nezávislé proměnné/objekty ořezávání zámků (lock stripping) RW zámky Neprovádět object pooling na jednoduchých objektech new je levnější jako malloc synchronized (new Object()) { 2 System.out.println("bleeee"); } 41/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Omezování zámků zrychlení 1 s + 1-s n JVM se snaží dělat eliminaci synchronizací, které nemohou nastat (např. pomocí escape analysis) kombinace více zámků do jednoho (lock coarsening) Zbytečně nesynchronizovat delegace bezpečnosti (thread safety delegation) omezení rozsahu synchronizace (get in ­ get out principle, např. Taxi/Dispatcher) dělení zámků (lock splitting) ­ pouze pro nezávislé proměnné/objekty ořezávání zámků (lock stripping) RW zámky Neprovádět object pooling na jednoduchých objektech new je levnější jako malloc synchronized (new Object()) { 2 System.out.println("bleeee"); } 42/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Omezování zámků Podobně jako dělení zámků, ale pro proměnný počet nezávislých proměnných/objektů Příklad ořezávání zámků ­ ConcurrentHashMap 16 zámků každý z N hash buckets je chráněný zámkem N mod 16 předpokládáme rovnoměrné rozdělení položek mezi kbelíky 16 paralelních přístupů přístup k celé kolekci vyžaduje všech 16 zámků rozdělení kumulativních polí do jednotlivých kbelíků 43/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Interakce s JVM při měření Problém garbage collection -verbose:gc krátká měření: vybrat pouze běhy, v nichž nedošlo ke GC dlouhé běhy: dostatečně dlouhé, aby se přítomnost GC projevila representativně Problém HotSpot kompilace -XX:+PrintCompilation dostatečný warm-up (minuty!) mohou se vyskytovat rekompilace (optimalizace, nahrání nové třídy která zruší dosavadní předpoklady) housekeeping tasks: oddělení nesouvisejících měření pauzou nebo restartem JVM 44/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Několik rad a myšlenek k měření Einmal is keinmal! Průměr x = N i=1 xi N (1) Směrodatná neboli standardní odchylka x = 1 N - 1 N i=1 (xi - x)2 = 1 N - 1 N i=1 x2 i - Nx2 (2) Směrodatná odchylka aritmetického průměru x = x N (3) Ze směrodatné odchylky aritmetického průměru mimo jiné také plyne její závislost na počtu vzorků, tj. chceme-li zvýšit přesnost dvakrát, musíme zvětšit velikost vzorku čtyřikrát. 45/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Několik rad a myšlenek k měření Odhad spolehlivosti pro normální rozdělení Polointerval Úroveň spolehlivosti [%] 68.27 1.96 95.0 2 95.45 3 99.73 4 99.99 Běžně se uvádí přesnost na 95 %, tedy pro normální rozdělení x 1.96x (1) 46/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Několik rad a myšlenek k měření Centrální limitní věta: Pro velký počet nezávislých proměnných se stále stejným rozdělením bude jejich rozdělení přibližně odpovídat rozdělení normálnímu, a to za předpokladu, že náhodné proměnné mají konečný rozptyl. 47/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Několik rad a myšlenek k měření Máme normální rozdělení? šikmost (skewness ­ třetí moment) 1 resp. g1 pro vzorky špičatost (kurtosis ­ čtvrtý moment) 2 resp. g1 pro vzorky g1 = m3 m 3 2 2 = 1 n i=1 N(xi - x)3 1 n i=1 N(xi - x)2 3 2 (1) g2 = m4 m2 2 - 3 = 1 n i=1 N(xi - x)4 1 n i=1 N(xi - x)2 2 - 3 (2) g1 = 0 normální g1 < 0 více vzorků je nalevo g1 > 0 naopak napravo g2 = 0 normální g2 < 0 těžké konce rozdělení g2 > 0 lehké konce 48/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Několik rad a myšlenek k měření Skripta Fr. Šťastného http://amper.ped.muni.cz/jenik/nejistoty/ 49/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy long promenna = 10000000L; 50/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Paměťový model Javy happens-before částečné uspořádání Tabulka převzata z JCiP, Goetz 51/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Paměťový model Javy Tabulka převzata z JCiP, Goetz 52/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Paměťový model Javy Piggybacking spojení happens-before pravidla s jiným pravidlem, obvykle monitorem nebo volatile raději nepoužívat příklad: http://kickjava.com/src/java/util/ concurrent/FutureTask.java.htm postaveno na tryReleaseShared happens-before tryAcquireShared kombinace volatilní proměnné runner, do které tryReleaseShared zapisuje s program order 53/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Paměťový model Javy void innerSet(V v) { 2 for (;;) { int s = getState(); 4 if (ranOrCancelled(s)) return; 6 if (compareAndSetState(s, RAN)) break; 8 } result = v; 10 releaseShared(0); done(); 12 } V innerGet() throws InterruptedException JavaDoc, ExecutionException Jav 2 acquireSharedInterruptibly(0); if (getState() == CANCELLED) 4 throw new CancellationException JavaDoc(); if (exception != null) 6 throw new ExecutionException JavaDoc(exception); return result; 8 } 54/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Paměťový model Javy líná inicializace 1 public class UnsafeLazyInitialization { private static Resource resource; 3 public static Resource getInstance() { 5 if (resource == null) resource = new Resource(); // unsafe publication 7 return resource; } 9 static class Resource { 11 } } 55/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Paměťový model Javy @ThreadSafe 2 public class SafeLazyInitialization { private static Resource resource; 4 public synchronized static Resource getInstance() { 6 if (resource == null) resource = new Resource(); 8 return resource; } 10 static class Resource { 12 } } líná inicializace thread-safe 56/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Paměťový model Javy @ThreadSafe 2 public class EagerInitialization { private static Resource resource = new Resource(); 4 public static Resource getResource() { 6 return resource; } 8 static class Resource { 10 } } ,,dychtivá`` inicializace využívá skutečnosti, že statické inicializátory jsou vždy dokončeny před použitím třídy 57/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Paměťový model Javy @ThreadSafe 2 public class ResourceFactory { private static class ResourceHolder { 4 public static Resource resource = new Resource(); } 6 public static Resource getResource() { 8 return ResourceFactory.ResourceHolder.resource; } 10 static class Resource { 12 } } idiom líné inicializace s použitím holder class využívá líné inicializace tříd 58/58 ThreadPoolExecutors Revisited Uváznutí Optimalizace výkonu Paměťový model Javy Paměťový model Javy public class DoubleCheckedLocking { 2 private static Resource resource; 4 public static Resource getInstance() { if (resource == null) { 6 synchronized (DoubleCheckedLocking.class) { if (resource == null) 8 resource = new Resource(); } 10 } return resource; 12 } Double Checked Locking anti-pattern pomíjí možnost, že resource je v nede novaném stavu od Java 5.0 možno spravit použitím volatile nepoužívat ani v C/C++!