Java Thread Fundamentals

Thread Safety:

Single-threaded operations, no concurrency, typically found in older API versions.

StringBuffer

Vector

HashTable

Non-thread-safe:

Multi-threaded concurrent operations, generally newer versions that improve performance but introduce concurrency issues.

StringBuilder

ArrayList

HashMap

The execution order of threads primarily depends on CPU scheduling. We can only indirectly control certain special cases through specific methods, as the CPU ultimately allocates resources.

Thread States:

Thread Creation (thread exists) - Ready State (can execute, waiting for CPU allocation, transitions to running upon resource assignment) - Running State (executing thread, performing code operations) - Waiting/Suspended (CPU resources exhausted or manually suspanded for other threads to run, returns to ready state after complesion) - Exception/Terminated (exception occurs or forced termination, previously using stop() method which is now deprecated)

Creating Threads: new Thread

Ready State: start()

Running State: run() // CPU executes automatically; manual invocation makes it run completely before other threads (becoming single-threaded)

Waiting/Suspended: wait() ---》 notify, notifyAll methods wake up and return to ready state

Exception/Terminated: Exception thrown or manual termination (stop() deprecated), thread ends

Thread Implementation Methods:

Define a class

Inherit from Thread class (Java's thread class)

Override run method

Create thread object (enters ready state).

Wait for CPU time allocation (sometiems called time slices).

Then let it execute automatically or control its behavior with methods like sleep.

This covers basic implementation

package thread;

public class ThreadDemo {
    public static void main(String[] args){

        //First, don't run all three examples simultaneously, as it creates 9 concurrent threads and obscures results.
        //Run groups of 3 threads at a time instead.

        //Classes used: Athlete
        //Create threads
        Athlete a1 = new Athlete("Bolt");
        Athlete a2 = new Athlete("Liu Xiang");
        Athlete a3 = new Athlete("Su Bingtian");
        //Ready state
        a1.start();
        a2.start();
        a3.start();
        //Running state
        //Thread execution is managed by the CPU, we cannot directly control it.
        //CPU allocates resources when available and executes threads.
        //Bolt ran to 0 meters
        //Bolt ran to 1 meter
        //Bolt ran to 2 meters
        //Bolt ran to 3 meters
        //Bolt ran to 4 meters
        //Bolt ran to 5 meters
        //Bolt ran to 6 meters
        //Bolt ran to 7 meters
        //Bolt ran to 8 meters
        //Bolt ran to 9 meters
        //Bolt ran to 10 meters
        //Bolt ran to 11 meters
        //Bolt ran to 12 meters
        //Bolt ran to 13 meters
        //Bolt ran to 14 meters
        //Liu Xiang ran to 0 meters
        //Liu Xiang ran to 1 meter
        //Liu Xiang ran to 2 meters
        //...
        //Results vary based on computer performance (better computers show more consecutive executions for one object).
        //Same computer shows different results each run.
        //Results confirm that the CPU alternates between threads, causing concurrent interleaved execution.
        //-.- I used to think multi-threading meant multiple threads executing simultaneously... now I understand it's logical concurrency, not actual parallelism.

        System.out.println("-------------------------I am a beautiful separator 1---------------------------------");
        //Classes used: RaceRunner

        //Create threads
        RaceRunner r1 = new RaceRunner("Bolt");
        RaceRunner r2 = new RaceRunner("Liu Xiang");
        RaceRunner r3 = new RaceRunner("Su Bingtian");

        Thread t1 = new Thread(r1); //start method belongs to Thread class, not Runnable interface, so we need to wrap the object in a Thread constructor
        Thread t2 = new Thread(r2);
        Thread t3 = new Thread(r3);
        //Ready state
        t1.start();
        t2.start();
        t3.start();
        //Running state
        //Execution results resemble those created by extending Thread class.

        System.out.println("-------------------------I am a beautiful separator 2---------------------------------");
        //12306 Train Ticket Sales System
        //Classes used: TicketCounter, System12306,Ticket
        TicketCounter tc1 = new TicketCounter("Ticket Window 1");
        TicketCounter tc2 = new TicketCounter("Ticket Window 2");
        TicketCounter tc3 = new TicketCounter("Ticket Window 3");

        tc1.start();
        tc2.start();
        tc3.start();

        //Only showing last few lines
        //Ticket Window 3 sold a ticket: from Shanghai51 to Beijing51 priced at 400.0
        //Ticket Window 1 sold a ticket: from Shanghai93 to Beijing93 priced at 400.0
        //Ticket Window 2 sold a ticket: from Shanghai92 to Beijing92 priced at 400.0
        //Ticket Window 1 sold a ticket: from Shanghai95 to Beijing95 priced at 400.0
        //Ticket Window 3 sold a ticket: from Shanghai94 to Beijing94 priced at 400.0
        //Ticket Window 1 sold a ticket: from Shanghai97 to Beijing97 priced at 400.0
        //Ticket Window 2 sold a ticket: from Shanghai96 to Beijing96 priced at 400.0
        //Ticket Window 1 sold a ticket: from Shanghai99 to Beijing99 priced at 400.0
        //Ticket Window 3 sold a ticket: from Shanghai98 to Beijing98 priced at 400.0
        //Sorry, Ticket Window 3 has sold out
        //Sorry, Ticket Window 2 has sold out
        //Sorry, Ticket Window 1 has sold out

    }

}

class Athlete

package thread;

public class Athlete extends Thread {
    private String athleteName;

    public Athlete(){}

    public Athlete(String name){
        this.athleteName = name;
    }

    public void run(){ //Override run method
        for(int i = 0; i < 100; i++) {  //Used to display thread progress
            System.out.println( athleteName + " ran to " + i + " meters");

        }
    }
}

class RaceRunner

package thread;

public class RaceRunner implements Runnable { //Runnable is an interface containing only the run method, mainly to indicate that this class can act as a thread, similar to Serializable, allowing inheritance from other classes (Java allows only one parent class but multiple interfaces)
    private String athleteName;

    public RaceRunner(){}

    public RaceRunner(String name){
        this.athleteName = name;
    }

    public void run(){ //Override run method
        for(int i = 0; i < 100; i++) {  //Used to display thread progress
            System.out.println( athleteName + " ran to " + i + " meters");
        }
    }
}

class System12306

package thread;

import java.util.ArrayList;
import java.util.Vector;

//Represents 12306 system
//Since there's only one system, use singleton pattern design
//Singleton pattern notes: https://www.cnblogs.com/clamp7724/p/11604422.html
public class System12306 {
    //Singleton pattern, globally only one system instance, accessible directly
    //Assume only one train route, real systems would be more complex

    private System12306(){} //Private constructor
    private static System12306 systemInstance = new System12306(); //Only one external class can operate this object
    public static System12306 getInstance(){ //Provide interface for external object creation to access this class (all accesses are to the same object, achieving singleton effect)
        return systemInstance;
    }

    //Container for tickets
    private Vector<Ticket> tickets = new Vector<>(); //Since object is unique, static keyword doesn't matter

    {   //Initialization block, runs before constructor
        for(int i = 0; i < 100; i++){ //Assume 100 tickets available
            tickets.add(new Ticket("Shanghai" + i, "Beijing" + i, 400.0f));
        }
    }

    //Issue ticket
    public Ticket getTicket() {
        try {
            return tickets.remove(0); //Remove and return first ticket from list
        }catch (Exception e){ //If no tickets left, may throw exception, return null. Could also add size check
            return null;
        }
    }
}

class TicketCounter

package thread;

//Multiple windows selling train tickets to demonstrate multi-threading
public class TicketCounter extends Thread{//Must inherit Thread class
    private String windowName;

    public TicketCounter(String windowName){
        this.windowName = windowName;
    }

    public void run(){ //Override run method, controls what happens during execution
        sellTickets();  //Continuously sell tickets during execution
    }

    public void sellTickets(){
        while(true){
            System12306 system = System12306.getInstance(); //Get 12306 instance
            Ticket ticket = system.getTicket(); //Vector is thread-safe, so no locking needed for multi-threading conflicts.
            if(ticket == null){
                System.out.println("Sorry, " + windowName + " has sold out");
                break;
            }
            else{
                System.out.println(windowName + " sold a ticket: " + ticket); //toString method overridden, so direct object printing works
            }
        }
    }


    public String getWindowName() {
        return windowName;
    }

    public void setWindowName(String windowName) {
        this.windowName = windowName;
    }
}

class Ticket

package thread;

//Stores train ticket information
public class Ticket {
    private String departureStation;
    private String arrivalStation;
    private Float price = null;
    //Note: If a class serves merely as a container with no special functionality (only attributes and simple methods), it's called a POJO or JavaBean.
    //- - Various online explanations about JavaBeans are essentially these types of classes.
    //Database fields might contain null values, using primitive types like float would cause errors, so use corresponding wrapper classes instead. Wrapper classes support direct assignment and provide better program robustness.
    //8 primitive types:              byte,short,int,long,float,double,boolean,char
    //Their wrapper classes:          Byte,Short,Integer,Long,Float,Double,Boolean,Character

    public Ticket(){
    }

    public Ticket(String departureStation, String arrivalStation, Float price){
        this.departureStation = departureStation;
        this.arrivalStation = arrivalStation;
        this.price = price;
    }

    public String toString(){ //Override toString method for easy printing. System.out.println() calls toString by default when printing objects
        return "from " + departureStation + " to " + arrivalStation + " priced at " + price;
    }

    //Right-click --- Generate --- Getters and Setters --- Select properties to generate getters and setters
    public String getDepartureStation() {
        return departureStation;
    }

    public void setDepartureStation(String departureStation) {
        this.departureStation = departureStation;
    }

    public String getArrivalStation() {
        return arrivalStation;
    }

    public void setArrivalStation(String arrivalStation) {
        this.arrivalStation = arrivalStation;
    }

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }
}

Tags: java thread Synchronization multithreading Concurrency

Posted on Mon, 13 Jul 2026 16:28:33 +0000 by yarnold