目录
同步的好处:
解决了多线程的安全问题
同步的弊端:
加入了同步之后,就相当于加了一把锁,这样每次进入同步代码块之前都会判断一下,无形之中,降低了程序的运行效率
1、同步代码块的锁对象是谁?
任意对象
2、同步方法的时候,锁对象又是谁呢?
this
3、如果是静态同步方法,锁对象是该静态同步方法所属那个类的字节码文件,字节码文件也属于一种对象。
我们之前解决线程同步安全的时候,是使用synchronized关键字,通过我们的分析得出将哪些代码块给包起来,但是,我们并没有直接看到哪里上了锁,在哪里释放了锁让其他线程拿到。为了更清晰的表达如何加锁以及释放锁,在JDK1.5之后提供了一个新的锁对象Lock
Lock:(接口)
具体的子类:ReentrantLock
void lock() 获得锁
void unlock() 释放锁
哪些是线程安全的类:
Vector虽然是线程安全的,我们今后开发也不用它,
工具类,用该工具类下面方法得到的所有集合都是可以线程安全的:
Collections
死锁问题:
两个或者两个以上的线程在争夺资源的过程中,出现了相互等待的现象,叫死锁现象。
同步的弊端:
1、效率低
2、容易产生死锁
线程间的通信
针对同一个资源的操作有不同种类的线程
可能出现的问题和原因:
1、同一个数据连续出现了很多次
CPU一点点的时间片就足以线程执行很多次
2、姓名和年龄与我们给的不匹配
这是由线程运行具有随机性导致的
3、存在线程安全问题:
1)是否存在多线程环境 是
2)是否存在共享数据 是
3)是否有多条语句操作着共享数据 是
都满足,说明存在线程安全问题
解决方案:
加锁
注意:
1、不同种类的线程类中都需要加锁
2、不同种类的线程类中的锁对象必须要是同一个
我们虽然解决了线程安全问题,但是程序中存在等待唤醒机制的问题。加入等待唤醒机制。
如何添加等待唤醒机制呢?
Object类中存在三个方法:
void wait() 导致当前线程等待,直到另一个线程调用该对象 notify()方法或 notifyAll()方法
void notify() 唤醒正在等待对象监视器的单个线程
void notifyAll() 唤醒正在等待对象监视器的所有线程
为什么这些方法不定义在Thread类中?
这些方法想要调用,必须通过锁对象进行调用,而我们知道锁对象可以是任意锁对象,所以这些方法,就必须定义在Object类中。
线程组:把多个线程组合到一起
JAVA中使用ThreadGroup来表示线程组
它可以对一批线程进行分类管理
JAVA允许程序直接对线程组进行控制的
线程池中的每一个线程代码结束后,并不会死亡
而是再次回到线程池中成为空闲状态,等待下一个对象来使用
如何实现线程池的代码呢?
1、创建线程池对象,Executors工厂类下的静态方法,newFixedThreadPool是其中一种线程池
public static ExecutorService newFixedThreadPool(int nThreads)
2、往线程池中放线程:(可以放哪些线程?)
3、在线程池中的线程怎么运行呢?
4、我想结束线程的运行,可不可以手动结束?如果可以怎么结束
shutdown()结束线程。
多线程的实现方式三:实现Callable接口,重写call方法,结合线程池运行
Future submit(Callable task)
提交值返回任务以执行,并返回代表任务待处理结果的Future
常见的情况:
1、新建--就绪--运行--死亡
2、新建--就绪--运行--就绪--运行--死亡
3、新建--就绪--运行--等待阻塞--同步阻塞--就绪--运行--死亡
4、新建--就绪--运行--同步阻塞--就绪--运行--死亡
5、新建--就绪--运行--其他阻塞--就绪--运行--死亡
============================================
总结
多线程:
指的一个程序中有多条执行路径的情况,我们称之为多线程程序
多线程的实现方式:
1、继承Thread类,重写run()方法,调用start()方法启动
2、实现Runnable接口,重写run()方法,创建Thread对象把Runnable对象当作参数传递,
调用start()方法启动
3、实现Callable接口,重写call()方法,提交到线程池中运行,需要结合线程池
newFixedThreadPool
newCachedThreadPool
newSingleThreadExecutor()
newScheduledThreadPool
线程的同步安全问题:
造成线程同步问题的原因:(三个条件同时存在)
1、是否存在多线程环境
2、是否存在共享数据/共享变量
3、是否有多条语句操作共享数据/共享变量
解决线程的同步安全问题方案
方案一:
同步代码块:
格式:synchronized(锁对象){
需要同步的代码(操作共享数据/共享变量的代码块)
}
锁对象的使用:
注意:发生同步安全的多线程之间的锁对象要一致,锁对象唯一
正常情况下:锁对象是任意对象
同步方法:锁对象是this
同步静态方法:锁对象是当前类的字节码文件对象
方案二:
Lock锁,JDK1.5之后出现
lock() 加锁
unlock() 释放锁
生产者消费者问题:
解决方案:
等待唤醒机制:(前提是线程没有同步安全问题)
Object类下提供了三个方法:
wait()
notify()
notifyAll()
线程组:将多个线程分组,放到一组,程序可以直接操作线程组(比如设置守护线程)
线程池:
好处:
线程池里的每一个线程代码结束后,并不会死亡,
而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
匿名内部类的形式创建线程对象并启动