1. Implement the Runnable interface
class MyThread implements Runnable {
    public void run() {
        System.out.println("I am running " + Thread.currentThread().getName());
    }
}

public static void main(String[] args) {
     for (int i = 0; i < 10; i++) {
         Thread t = new Thread(new MyThread());
         t.setName("" + i);
         t.start();
     }
 }

2. Extend the Thread class

class MyOtherThread extends Thread {
    public void run() {
       System.out.println("I also want to run" + this.getName());
    }
}

public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        MyOtherThread t = new MyOtherThread();
        t.setName("" + i);
        t.start();
    }
}

3. Implement Callable interface and use the Executor framework.

class MyCallableThing implements Callable<String> {
    public String call() {
        System.out.println("I am a callable thing running " + Thread.currentThread().getName());
        return Thread.currentThread().getName();
    }
}

public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(10);
    List<Future<String>> storeTheFutures = new ArrayList<Future<String>>();
    Callable<String> callableThing = new MyCallableThing();
    for (int i = 0; i < 10; i++) {
        Future<String> future = executor.submit(callableThing);
        storeTheFutures.add(future);
    }
    for (Future<String> f : storeTheFutures) {
        try {
            System.out.println("The value is future is " + f.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}