A semaphore in Java is used to limit access to shared resources by threads. It maintains a counter to control the number of threads that can access the resource at one time. When the counter is greater than 0, access is granted; otherwise access is denied. The Semaphore class provides methods like acquire() and release() to increment and decrement the counter. Semaphores help avoid race conditions by allowing synchronized access to resources between threads.