标签 :java基础
1. Java多线程的实现
(1)继承Thread类,重写run方法。
(2)实现Runnable接口,重写run方法。(这个设计比较好)
本质上Thread类就是实现了Runable的类。
// 创建线程
Runnable task = new TaskClass();
// 开启线程
new Thread(task).start();
2. 线程池使用
使用ExecutorService类和Executors类实现线程池,代码如下:
// 固定线程数的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 按需创建新线程的线程池
ExecutorService executor2 = Executors.newCachedThreadPool();
// 使用线程池
executor.execute(new TaskClass());
// 关闭线程池
executor.shutdown();
3. 线程同步问题
(1)竞争状态问题:多个线程以冲突的方式访问一个公共资源。
(2)线程安全的类:该类的对象在多线程下没有导致竞争状态问题。
线程安全、不安全的类就是指这种公共资源类。
4. 解决线程同步问题
(1)给临界区方法加synchonized关键字。
一个线程访问这个方法时,整个对象锁住,其他线程访问这个对象的任意方法会阻塞,直到解锁。
(2)给需要同步的语句包上synchronized语句块。
好处是只需要锁住导致线程不安全的语句,而不是锁住整个方法。
synchronized(obj){
doSomething();
}
(3)显式使用锁。
实现了Lock接口的对象是一个锁对象。可以利用锁对象对其他对象显式加锁。例如ReentrantLock类,可以创建具有公平策略的锁,即那个线程先来那个先得。(默认不是公平锁)
使用方法,在需要实现同步访问的类中,组合一个Lock接口的实例,在临界区前调用lock.lock(),在结束后调用lock.unlock()。如下:
public void add(int a){
lock.lock();
try{
// 临界区
doSomething();
}
catch(InterruptedException e){
}
finally{
lock.unlock();
}
// 写try-catch这个习惯比较好,保证锁能释放
}
ReentrantLock类可以创建condition,比synchronzied关键字更灵活可控。
5. 线程间合作
(1)使用Condition接口的实例。Condition接口的实例从Lock对象生成,使用时必须获得该Condition的锁(否则抛出异常)。使用方法是把Condition的实例放在Lock实例的他同一个类底下。
// 创建条件
private static Condition condition = lock.newCondition();
// 阻塞当前线程,直到别人来唤醒
condition.await();
// 唤醒所有阻塞线程
condtition.signalAll();
(2)传统方法,监视器。即使用每个对象本身的wait(),notify(),notifyAll()等方法。
6. 生产者/消费者模型
(1)使用一个lock,两个condition(notEmpty和notFull),每次生产/消费时用lock锁住Buffer。
(2)如果生产时发现容器已满,需要等待notFull条件;
(3)同样,如果消费时发现容器为空,则需要等待notEmpty条件。
(4)每次生产完唤醒等待notEmpty条件的线程,每次消费完唤醒等待notFull条件的线程。