In Java, Semaphore is a class in the concurrency framework that provides a way to control access to a shared resource. It works by maintaining a count of available permits, which threads can acquire and release to access the shared resource.

Here is an example that demonstrates how to use Semaphore to control access to a shared resource. Suppose we have a list of tasks that need to be executed, and we want to limit the number of tasks that can be executed concurrently to a fixed number:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    private static final int NUM_TASKS = 10;
    private static final int MAX_CONCURRENT_TASKS = 3;

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(MAX_CONCURRENT_TASKS);
        ExecutorService executor = Executors.newFixedThreadPool(NUM_TASKS);

        for (int i = 0; i < NUM_TASKS; i++) {
            executor.execute(new Task(semaphore, i));
        }

        executor.shutdown();
    }

    private static class Task implements Runnable {
        private Semaphore semaphore;
        private int id;

        public Task(Semaphore semaphore, int id) {
            this.semaphore = semaphore;
            this.id = id;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("Task " + id + " acquired semaphore");
                Thread.sleep(1000);
                System.out.println("Task " + id + " releasing semaphore");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

In this example, we create a Semaphore with a maximum of MAX_CONCURRENT_TASKS permits, which represents the maximum number of tasks that can be executed concurrently. We also create an ExecutorService with a fixed thread pool of size NUM_TASKS.

We then submit NUM_TASKS tasks to the executor, each of which acquires the semaphore before executing and releases it when it is done. The Semaphore ensures that no more than MAX_CONCURRENT_TASKS tasks can acquire the semaphore at any given time, thus limiting the number of concurrent tasks.

In the Task class, the acquire method is called to acquire a permit from the Semaphore before the task is executed, and the release method is called to release the permit when the task is done. The sleep method is used to simulate the task executing for some time.

In summary, Semaphore is a useful class in the Java concurrency framework that allows you to control access to a shared resource and limit the number of threads that can access it concurrently.