1.回顾
使用Thread.currenThread.getName();获取线程名称。
【案例】线程生命周期
image.png
image.png
image.png
2.使用Runnable创建线程
方法一:继承Thread。
方法二:实现Runnable。
步骤:
(1)实现Runnable接口。
(2)实现run方法。(核心代码)
(3)将Runnable的实现类实例化,作为一个参数传入Thread类的构造方法中。
(4)使用Thread创建实例,启动线程。
【案例】
项目结构:
image.png
MyThread.java
image.png
MyTest.java
image.png
继承Thread创建线程和实现Runnable接口两种方法之间的差异:
【案例】如果去火车站买车票,车站一共有100张车票,通过4个窗口去销售,如何保证,每个窗口分配销售这100张车票。这100张相当于一个大的任务。每个窗口相当于这个任务由4个人中每个人。每个窗口可以看成是一个线程。
如何通过线程去模拟?
方法一:继承Thread.创建线程,线程间数据不共享,导致一票多卖。
即使在Thread中建立static变量,依然会出现一票多卖。
项目结构:
image.png
Seller.java
image.png
MyTest.java
image.png
日志:
image.png
为什么?
当窗口许诺给客户时,拿到车票可能是同一张车票,卖完了,将车票编号减一。每个窗口卖100张,只是编号一样。
使用static改造共享数据。修改后的Seller.java
image.png
日志:
image.png
方法二:实现Runnable接口。
区别:使用Runnable是可以共享数据区域的。互相不影响。使用thread创建线程,不会共享数据区域,如果使用static共享数据区域,会造成数据被篡改。
数据被不同线程篡改,会线程不安全。
项目结构:
image.png
Seller2.java
image.png
MyTest2.java
image.png
日志:
image.png
3.线程互斥
使用synchronized关键字,可以实现线程互斥,解决掉线程不安全。相当于对代码加锁,谁拥有钥匙,就谁来执行这个代码。当前占有这个代码的线程,就排斥其他线程来执行这个代码。
为什么要互斥?
线程占有某段代码,其他线程不能执行这个代码,保证线程安全。
如何实现互斥?
也就是线程加锁。
如何加锁?
synchronized,加锁有2种方式:
(1)对方法加锁
(2)对代码加锁
synchronized(lock){
....//被加锁的代码
}//lock是一个任意实例。
【案例】卖票的加锁解决方案。
修改Seller2.java
image.png
日志:
image.png
【作业】
写一段代码,模拟多线程下载。下载的目标文件假设是100M。每个线程下载速度是1秒1M,开启5个线程下载。每隔1秒要输出一下日志,已经下载多少,还有多少没有下载。
网友评论