一、基本概念
进程与线程的区别
- 进程是操作系统分配资源的最小单位,拥有独立的内存空间。
- 线程是CPU调度的最小单位,同一进程下的线程共享进程资源。
为什么要使用多线程
- 提高程序执行效率,充分利用CPU资源。
- 增强程序的响应性,避免程序在执行耗时操作时失去响应。
二、线程创建
继承Thread类
- 自定义一个类,继承自Thread类,重写run()方法。
- 创建该类的对象,并调用start()方法启动线程。
实现Runnable接口
- 定义一个类实现Runnable接口,实现其run()方法。
- 创建Thread类对象,将实现了Runnable接口的类对象作为参数传递给Thread类构造方法。
- 调用Thread对象的start()方法启动线程。
三、线程生命周期
- 新建(New):线程对象创建后,但尚未调用start()方法。
- 就绪(Runnable):线程对象调用了start()方法,等待CPU调度。
- 运行(Running):线程获得CPU控制权,执行run()方法中的代码。
- 阻塞(Blocked):线程因等待某些资源而被阻塞,如等待I/O操作完成。
- 等待(Waiting):线程因调用wait()方法而进入等待状态,需要其他线程唤醒。
- 计时等待(Timed Waiting):线程因调用sleep()或wait(long timeout)方法而进入计时等待状态。
- 终止(Terminated):线程执行完毕或因异常退出。
四、线程同步与通信
同步的概念
- 当多个线程访问共享资源时,需要确保同一时刻只有一个线程能访问该资源,以保证数据的一致性。
synchronized关键字
- 用于修饰方法或代码块,确保同一时刻只有一个线程能执行被修饰的方法或代码块。
wait()、notify()和notifyAll()方法
- wait():让当前线程释放对象锁,进入等待状态,直到其他线程调用相同对象的notify()或notifyAll()方法唤醒。
- notify():唤醒在该对象上等待的单个线程。
- notifyAll():唤醒在该对象上等待的所有线程。
五、线程间通信
join()方法
- 让当前线程等待另一个线程执行完毕,常用于主线程等待子线程执行完毕的场景。
生产者消费者问题
- 通过wait()、notify()或notifyAll()方法解决生产者和消费者之间的协调问题。
六、注意事项
避免死锁:死锁是指多个线程在执行过程中,因互相等待对方释放资源而导致的永久阻塞现象。可以通过按顺序申请资源、设置超时时间等手段避免死锁。
减少锁的竞争:尽量减少对共享资源的锁定时间和范围,降低线程竞争锁的概率。
合理选择线程数量:根据任务特点和系统资源,合理设置线程数量,避免过多线程导致的上下文切换开销。