线程是计算机中一种轻量级的进程,它是程序执行的基本单位,可以看做是进程中的一个小部分。
线程与进程不同,进程是操作系统进行资源分配和调度的基本单位,每个进程都有自己的独立内存空间和系统资源,进程之间相互独立。而线程是进程中的实际执行单位,一个进程中可以包含多个线程,这些线程共享同一份内存空间和资源。
一个线程拥有自己的ID、PC(程序计数器)、寄存器集合和栈区域等线程上下文(Thread Context),每个线程都有自己的状态,包括初始态、就绪态、运行态、阻塞态和死亡态。
在Java中,线程是由Thread类来实现的,也可以通过实现Runnable接口来创建线程,然后通过调用start()方法来启动线程的执行。线程执行完后会进入终止状态,不能再次启动执行。
线程是轻量级的执行单元,创建和销毁线程的开销很小。
线程可以并发执行,可以利用多核CPU提高程序的执行效率。
线程之间可以共享同一份数据空间,便于信息的共享和通信。
线程的执行顺序和优先级可以通过调度算法来控制。
由于线程之间共享同一份内存空间和资源,因此在多线程编程中需要注意线程安全问题,如对共享变量的并发访问可能带来的数据竞争和死锁等问题。
线程的使用可以大大提高程序性能和执行效率,可以将任务并发执行从而缩短响应时间和提高效率,但也要注意避免过多创建线程带来的负面影响。同时,需要合理地利用锁、原子操作等技术来保证线程安全和正确性。
线程的概念:线程是操作系统能够进行运算调度的最小单位,它被包含在进程中,是进程中的实际运作单位。
线程的状态:线程的状态包括新建状态、就绪状态、运行状态、阻塞状态和死亡状态。
线程的创建:Java提供了三种方式来创建线程,分别是继承Thread类、实现Runnable接口和使用Executors框架。其中,实现Runnable接口是比较推荐的方式,因为Java只支持单继承,而且实现Runnable的方式更符合“高内聚低耦合”的设计思想。
线程的同步:线程同步是指多个线程访问共享资源时的同步问题,Java提供了synchronized关键字和Lock接口来解决这一问题。
线程的通信:线程通信是指多个线程之间的信息传递和协作,Java提供了wait()、notify()和notifyAll()等方法实现线程通信。
线程安全是指多线程环境下程序始终能够正确地执行,不会出现数据不一致、死锁等问题。Java线程安全问题的产生通常涉及到以下几个方面:
共享数据:多个线程访问同一块数据时容易出现线程安全问题。
不可变数据:多个线程访问同一块数据时,如果数据是不可变的,则不会存在线程安全问题。
线程操作:对于同一块数据,在同一时刻只能由一个线程进行操作,不能同时由多个线程操作。
Java提供了synchronized关键字用于解决线程同步问题,synchronized关键字可以保证同一时刻只有一个线程执行该代码块或方法。除此之外,Java还提供了Atomic包、Lock接口等方式来实现线程同步。
线程池是指在程序启动时预先创建若干个线程,并将这些线程存储在一个线程集合中,需要时直接从中获取线程来执行任务,执行完毕后再放回线程集合中。线程池的好处是可以避免频繁创建与销毁线程的开销,提高程序的性能。Java提供了ThreadPoolExecutor类来实现线程池,开发者可以根据自己的需求配置线程池的大小、线程存活时间、队列类型等参数。
class MyThread extends Thread {@Overridepublic void run() {// 线程执行的业务逻辑}
}public class Main {public static void main(String[] args) {Thread thread = new MyThread();thread.start();}
}
class MyRunnable implements Runnable {@Overridepublic void run() {// 线程执行的业务逻辑}
}public class Main {public static void main(String[] args) {Runnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start();}
}
public class Main {public static void main(String[] args) {Thread thread = new Thread(() -> {// 线程执行的业务逻辑});thread.start();}
}
class MyObject {private int count;public synchronized void increase() {count++;}
}public class Main {public static void main(String[] args) {MyObject myObject = new MyObject();for (int i = 0; i < 10; i++) {new Thread(() -> {for (int j = 0; j < 1000; j++) {myObject.increase();}}).start();}// 等待所有子线程执行完毕while (Thread.activeCount() > 2) {Thread.yield();}System.out.println(myObject.getCount());}
}class MyObject {private int count;private Lock lock = new ReentrantLock();public void increase() {try {lock.lock();count++;} finally {lock.unlock();}}
}public class Main {public static void main(String[] args) {MyObject myObject = new MyObject();for (int i = 0; i < 10; i++) {new Thread(() -> {for (int j = 0; j < 1000; j++) {myObject.increase();}}).start();}// 等待所有子线程执行完毕while (Thread.activeCount() > 2) {Thread.yield();}System.out.println(myObject.getCount());}
}class MyObject {private int count;public synchronized void produce() throws InterruptedException {while (count >= 10) {wait();}count++;System.out.println(Thread.currentThread().getName() + " produce " + count);notifyAll();}public synchronized void consume() throws InterruptedException {while (count <= 0) {wait();}count--;System.out.println(Thread.currentThread().getName() + " consume " + count);notifyAll();}
}public class Main {public static void main(String[] args) {MyObject myObject = new MyObject();new Thread(() -> {for (int i = 0; i < 10; i++) {try {myObject.produce();} catch (InterruptedException e) {e.printStackTrace();}}}, "producer").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {myObject.consume();} catch (InterruptedException e) {e.printStackTrace();}}}, "consumer").start();}
}class MyObject {private int count;private Lock lock = new ReentrantLock();private Condition producerCondition = lock.newCondition();private Condition consumerCondition = lock.newCondition();public void produce() throws InterruptedException {try {lock.lock();while (count >= 10) {producerCondition.await();}count++;System.out.println(Thread.currentThread().getName() + " produce " + count);consumerCondition.signalAll();} finally {lock.unlock();}}public void consume() throws InterruptedException {try {lock.lock();while (count <= 0) {consumerCondition.await();}count--;System.out.println(Thread.currentThread().getName() + " consume " + count);producerCondition.signalAll();} finally {lock.unlock();}}
}public class Main {public static void main(String[] args) {MyObject myObject = new MyObject();new Thread(() -> {for (int i = 0; i < 10; i++) {try {myObject.produce();} catch (InterruptedException e) {e.printStackTrace();}}}, "producer").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {myObject.consume();} catch (InterruptedException e) {e.printStackTrace();}}}, "consumer").start();}
}// 创建一个具有固定线程数(5个)的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);// 向线程池提交一个任务(Runnable或Callable)
executor.submit(new Runnable() {@Overridepublic void run() {// 线程具体执行的操作}
});// 关闭线程池
executor.shutdown();上面的代码中,通过调用Executors.newFixedThreadPool()方法创建一个具有固定线程数的线程池,然后通过executor.submit()方法向线程池提交一个任务,最后通过executor.shutdown()方法关闭线程池。
除了newFixedThreadPool()方法以外,还有其他常见类型的线程池,如:
newCachedThreadPool():一个可以动态调整线程数量的线程池,不限制线程数量,会根据需要自动创建和销毁线程,适用于处理大量短时间的任务。
newSingleThreadExecutor():一个只有一个工作线程的线程池,适用于需要保证任务按照指定顺序(如FIFO、LIFO、优先级)执行的场合。
线程池的做法可以避免频繁创建和销毁线程带来的性能开销,提高了线程的复用和管理效率,同时也可以避免线程数量过多导致资源耗尽的风险。根据实际的业务需求,可以选择合适的线程池类型和线程数量配置。