for(int i=0;i<n;i++){
for(int j=i;j<n;j*=2){
...
}
}

 

O(n)

 

 

 

@Transactional(propagation = Propagation.REQUIRED)

public void transferFunds(Account from, Account to, BigDecimal amount) {

    debit(from, amount);    // Deduct from source

    credit(to, amount);     // Add to destination

    // If credit fails, debit automatically rolls back

}

 

 

synchronized(lock1) {
    synchronized(lock2) { }
}

synchronized(lock2) {
    synchronized(lock1) { }
}

 

T1 acquires lock1
T2 acquires lock2
T1 waits for lock2
T2 waits for lock1

Deadlock.



How to prove from thread dump?

Look for:

Found one Java-level deadlock:
=============================

Thread-1:
 waiting to lock <0x111> held by Thread-2

Thread-2:
 waiting to lock <0x222> held by Thread-1

Use:

jstack PID
kill -3 PID

 


Banking redesign

Use consistent lock ordering.

Example:

Account first = from.getId() < to.getId()
        ? from : to;

Account second = from.getId() < to.getId()
        ? to : from;

synchronized(first) {
    synchronized(second) {
        transfer();
    }
}


Even better:

SELECT ... FOR UPDATE



Is volatile atomic?

No.

Volatile provides:

Visibility
Ordering

NOT atomicity.


AtomicInteger count =
new AtomicInteger();

count.incrementAndGet();


synchronized


newFixedThreadPool(5)

Answers

1. Danger?

Yes

ExecutorService pool = Executors.newFixedThreadPool(5);

// If you submit tasks faster than 5 threads can process,

// the internal queue grows indefinitely.

for (int i = 0; i < 1_000_000; i++) {

    pool.submit(() -> heavyTask()); // Queue keeps growing

}

  • RiskOutOfMemoryError (queue holds millions of pending tasks)
  • Default queueLinkedBlockingQueue (unbounded)


Hidden failure propagation

java

Future<?> future = pool.submit(() -> {

    throw new RuntimeException("Database down");

});

// If you never call future.get(), you never see the exception

  • Failures can be silently ignored



How to mitigate

Replace with explicit ThreadPoolExecutor:

ThreadPoolExecutor pool = new ThreadPoolExecutor(

    5, 5,                    // core, max

    0L, TimeUnit.MILLISECONDS,

    new ArrayBlockingQueue<>(100),  // Bounded queue

    new ThreadPoolExecutor.CallerRunsPolicy() // Backpressure

);

Or use newCachedThreadPool() for many short tasks

ExecutorService pool = Executors.newCachedThreadPool();

// Creates threads as needed, reuses idle ones

 

 

Monitor queue size:

ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);

if (pool.getQueue().size() > 1000) {

    // Reject new tasks or log warning

}

 

Danger

Why

Unbounded queue

Can cause OutOfMemoryError

Deadlock

Tasks waiting for each other

No backpressure

System overload without rejection

Silent failures

Exceptions lost if get() not called

Resource underutilization or overutilization

Wrong thread count for workload

 

LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(3);

       

        // Adding elements

        queue.put("A");     // Blocks if full

        queue.offer("B");   // Returns false if full

        queue.add("C");     // Throws exception if full