一、什么是线程
线程是操作系统进行任务调度的最小单位。在Java中,线程是一种轻量级的进程,拥有自己的执行路径,可以与其他线程共享进程中的资源。 Java中的线程分为两类:用户线程和守护线程。其中,用户线程是进程中的主线程,当所有用户线程结束时,进程才会结束。守护线程则是为其他线程提供服务,当所有用户线程结束时,守护线程也会随之结束。
二、如何创建线程
Java中有两种方式可以创建线程:
1. 继承Thread类
public class MyThread extends Thread {
public void run() {
// 线程执行逻辑
}
}
// 创建线程并启动
MyThread t = new MyThread();
t.start();
2. 实现Runnable接口
public class MyRunnable implements Runnable {
public void run() {
// 线程执行逻辑
}
}
// 创建线程并启动
Thread t = new Thread(new MyRunnable());
t.start();
三、线程的状态
Java中的线程有6种状态:
1. 新建状态(New)
当创建线程对象时,线程处于新建状态。
2. 就绪状态(Runnable)
当线程被创建后,它将进入就绪状态,等待系统调度执行。
3. 运行状态(Running)
当线程获得CPU时间片后,它进入运行状态,在此状态下线程执行具体的任务。
4. 阻塞状态(Blocked)
当线程由于某些原因无法获得所需的资源时,它将进入阻塞状态,直到资源被释放。
5. 等待状态(Waiting)
当线程调用wait()方法等待某个条件满足时,它将进入等待状态,直到被唤醒。
6. 终止状态(Terminated)
当线程执行完毕或遇到异常时,它将进入终止状态。
四、线程同步
线程同步是指多个线程在访问共享资源时,通过互斥机制协调彼此之间的关系,以避免数据不一致或其他并发问题。
1. synchronized关键字
synchronized
关键字是Java提供的最基本的线程同步机制,它可以用于修饰方法或代码块。使用synchronized
关键字修饰后,同步对象(也称锁对象)将会成为区分线程的标志。
public synchronized void method() {
// 有且仅有一条线程访问此方法
}
public void method() {
synchronized (this) {
// 有且仅有一条线程访问此代码块
}
}
2. Lock接口
Lock
接口也是一种线程同步机制,它提供了更加灵活和强大的同步方式。与synchronized
关键字相比,Lock
接口可以实现多个线程同时访问同一资源,通过tryLock
方法可以避免死锁等问题。
Lock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 获得锁,执行线程同步代码
} finally {
lock.unlock();
}
}
五、线程池
线程池是一种预先创建好的线程集合,在任务到来时,从集合中取出一个线程来处理任务,可以避免频繁创建和销毁线程,从而提升代码执行效率。
1. Executors类
Java提供了Executors
工厂类,可以快速创建常用的线程池。Executors
提供的线程池有以下几种:
a. newFixedThreadPool
创建一个固定大小的线程池,池中的线程数量始终不变。
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(new MyTask());
b. newCachedThreadPool
创建一个大小不受限制的线程池,池中线程数量可根据任务数量自动调整。
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new MyTask());
c. newSingleThreadExecutor
创建一个单线程的线程池,它保证所有任务按顺序执行。
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(new MyTask());
2. ThreadPoolExecutor类
除了Executors
工厂类提供的线程池之外,还可以通过ThreadPoolExecutor
类自定义线程池。ThreadPoolExecutor
提供了非常灵活的线程池配置项,包括核心线程数、最大线程数、线程存活时间、拒绝策略等。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 5, 60, TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(),
new ThreadPoolExecutor.AbortPolicy()
);
executor.execute(new MyTask());
六、线程通信
线程通信是指多个线程之间通过共享变量进行数据传递和同步的过程。
1. wait()和notify()方法
wait()
方法用于等待某个条件的发生,会使当前线程释放占有的锁,并进入等待队列中等待被唤醒。notify()
方法用于唤醒等待在等待队列中的一个线程。
public synchronized void producer() throws InterruptedException {
while (count == BUFFER_SIZE) {
// 缓冲区已满,等待消费者消费
wait();
}
// 生产数据
buffer[in] = data;
in = (in + 1) % BUFFER_SIZE;
count++;
// 唤醒等待在等待队列中的消费者
notify();
}
public synchronized void consumer() throws InterruptedException {
while (count == 0) {
// 缓冲区为空,等待生产者生产
wait();
}
// 消费数据
data = buffer[out];
out = (out + 1) % BUFFER_SIZE;
count--;
// 唤醒等待在等待队列中的生产者
notify();
}
2. Condition接口
Condition
接口是Java提供的高级线程同步机制,它可以让线程在等待某个条件满足时进入等待状态,而不是一直占用CPU资源。
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void method() throws InterruptedException {
lock.lock();
try {
while (condition不满足) {
condition.await();
}
// 线程执行逻辑
} finally {
lock.unlock();
}
}
public void changeCondition() {
lock.lock();
try {
// 改变condition的值
condition.signalAll();
} finally {
lock.unlock();
}
}
七、总结
Java线程编程是Java开发中必不可少的一部分,在并发编程中,线程的创建、状态、同步、池和通信等方面都需要掌握。对于初学者来说,建议从基础入手,逐渐提高对多线程编程的理解和实战经验。