一、创建多线程
1.实现Runnable接口
2.继承Thread类
3.匿名类
Runnable与Thread区别:一个是接口一个是类
二、线程安全的类
1.HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
区别1:
HashMap可以存放 null
Hashtable不能存放null
区别2:
HashMap不是线程安全的类
Hashtable是线程安全的类
2.StringBuffer 是线程安全的 多线程中保证数据的安全性
StringBuilder 是非线程安全的 大量字符串拼接操作时,单线程就用StringBuilder会更快些
非线程安全的为什么会比线程安全的快? 因为不需要同步
3.ArrayList和Vector的区别: Vector是线程安全的类,而ArrayList是非线程安全的
ArrayList是非线程安全的,换句话说,多个线程允许同时进入一个ArrayList对象的add方法
Collections.synchronizedList(),可以把ArrayList转换为线程安全的List
三、死锁
- 线程1 首先占有对象1,接着试图占有对象2
- 线程2 首先占有对象2,接着试图占有对象1
- 线程1 等待线程2释放对象2
- 与此同时,线程2等待线程1释放对象1
就会。。。一直等待下去,直到天荒地老,海枯石烂,山无棱 ,天地合。。。
四、线程交互
方法中我们如果使用while循环判断是否是1,会大量占用CPU,降低性能,所以使用以下方法解决该问题
1.wait() 调用wait一定是在synchronized块里,让占用了这个同步对象的线程,临时释放当前的占用,并且等待
2.notify() 通知一个等待在这个同步对象上的线程,你可以苏醒过来了,有机会重新占用当前对象了
3.notifyAll() 通知所有等待在这个同步对象上的线程,你们可以苏醒过来了,有机会重新占用当前对象了
wait方法和notify方法,并不是Thread线程上的方法,它们是Object上的方法。所有的Object都可以被用来作为同步对象,所以准确的讲,wait和notify是同步对象上的方法。
四、线程池
`ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue
threadPool.execute(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("任务1");
}
});//添加任务`
五、Lock对象解决可能会死锁的问题
1.Lock是一个接口,为了使用一个Lock对象,需要用到
Lock lock = new ReentrantLock();
2.与 synchronized (someObject) 类似的,lock()方法,表示当前线程占用lock对象,一旦占用,其他线程就不能占用了。
3.与 synchronized 不同的是,一旦synchronized 块结束,就会自动释放对someObject的占用。 lock却必须调用unlock方法进行手动释放,为了保证释放的执行,往往会把unlock() 放在finally中进行。
4.trylock方法:trylock会在指定时间范围内试图占用,如果时间到了,还占用不成功,就...
5.Lock也提供了对应synchronized方式进行线程交互的解决办法,首先通过lock.newCondition()得到一个Condition对象,然后分别调用这个Condition对象的:await, signal,signalAll 方法
6.Lock和synchronized的区别:
(1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现,Lock是代码层面的实现。
(2)Lock可以选择性的获取锁,如果一段时间获取不到,可以放弃。synchronized不行,会一根筋一直获取下去。 借助Lock的这个特性,就能够规避死锁,synchronized必须通过谨慎和良好的设计,才能减少死锁的发生。
(3)synchronized在发生异常和同步块结束的时候,会自动释放锁。而Lock必须手动释放, 所以如果忘记了释放锁,一样会造成死锁