多线程
1、多线程的实现之继承Thread类
(1)多线程的Run与Start方法
为什么重写run方法?
run是用来封装被线程执行的代码
my1.run()与my1.start()的区别?
线程对象调用run方法不开启线程。仅是对象调用方法。线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。
(2)多线程中设置和获取线程名称
设置:
(1)my1.setName方法
(2)有参构造,要在thread类中写有参构造方法
获得
(1)在继承的线程类中直接使用getName方法
(2)Thread.currentThread().getName
class MyThread extends Thread { //继承Thread
MyThread(String name){
super(name);
}
//复写其中的run方法
public void run(){
for (int i=1;i<=20 ;i++ ){
System.out.println(Thread.currentThread().getName()+",i="+i);
}
}
}
class ThreadDemo {
public static void main(String[] args) {
//创建两个线程任务
MyThread d = new MyThread();
MyThread d2 = new MyThread();
d.run();//没有开启新线程, 在主线程调用run方法
d2.start();//开启一个新线程,新线程调用run方法
}
}
(3)多线程的线程调度
l 分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
l 抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
设置和获取线程优先级
线程默认优先级为5,最小1,最大10
tp1.getPriority()
tp1.setPriority(5)
线程优先级高仅仅表示线程获取cpu时间片的几率高
(4)多线程的线程控制
thread.sleep() 使当前执行的线程暂停指定毫秒(线程类中)
t1.join() 等待这个线程死亡后,才执行其他的(测试类里)
t1.setDaemon() 将此线程设置为守护线程,主线程执行完后,运行的都是守护线程时,虚拟机将退出(测试类中)(不会立即死亡)
主线程是main方法,可以设置名字
(5)线程的生命周期
2、多线程的实现之Runnable接口
(1)Runnable接口介绍
实现Runnable接口的类就相当于thread的参数,看作资源
在MyRunnable中不能直接使用getName方法
名字作为参数传入
相比继承Thread类,实现Runnable接口的好处
避免了Java单继承的局限性,
适合多个相同程序的代码去处理同一个资源的情况,把线程和 程序的代码、数据有效分离,较好的体现
了面向对象的设计思想
(2)卖票案例
(3)卖票案例的思考
出票需要时间,故需要通过sleep方法模拟卖票时间,发现:
相同的票出现多次原因:
某线程苏醒后抢占到cpu的执行权后,输出卖票的语句后,可能还未改变ticket,令一个线程就抢到了cpu的执行权。
出现负数票:线程执行的随机性导致的
3、线程同步
(1)卖票案例为什么出现问题?![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0c566af819aa5ca81683c45dfe45af6d.png)
(2)同步代码块
同步代码块:一次只允许一个线程访问这个代码块
注意:括号里面的锁要定义到外面,不能定义到里面,要用同一把锁
同步代码块的好处和弊端:
(3)同步方法
同步方法:一次只允许一个线程访问这个方法
同步方法的格式:
在定义这个方法是加关键字,在代码块中加锁
同步方法的锁:是this,把之前的object改为this即可
同步静态方法:一次只允许一个线程访问这个静态方法
同步静态方法的锁:该类的class对象
(4)线程安全的类
在多线程的环境下使用的类
就是集合的一些安全类,相当于集合,只不过这些集合的方法线程安全,一次只允许一个线程访问这个方法。
但是:也可以通过这个方法将map,list变成线程安全类
(5)Lock锁
创建lock锁:
将lock锁加到代码块中: