多线程
一、线程、进程、并发、并行都是什么?
在开始多线程学习之前,说先需要了解一下线程、进程、并发、并行分别是什么呢?
线程 进程
专业的说法是:进程是资源分配的最小单位,线程是CPU调度的最小单位。
没懂?那做个简单的比喻:进程=火车,线程=车厢 。这下懂了吧,一个进程可以调用多个线程同时执行。
并发 并行
- 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
- 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力
二、多线程的创建方式
1.继承于Thread类
- 首先创建一个子类继承于Thread类
- 重写run()方法
- 创建Thread子类的对象
- 调用start()方法
四步解决!比把大象装冰箱只多一步,是不是很简单?
来一个窗口卖票小例子!
package com.qzh.studySE;
/**
* 创建三个窗口卖票,总票数为100张,使用继承自Thread方式
* 用静态变量保证三个线程的数据独一份
* 存在线程的安全问题,有待解决
* */
public class Main{
public static void main(String[] args){
window t1 = new window();
window t2 = new window();
window t3 = new window();
t1.setName("售票口--A");
t2.setName("售票口--B");
t3.setName("售票口--C");
t1.start();
t2.start();
t3.start();
}
}
class window extends Thread{
//将其加载在类的静态区,所有线程共享该静态变量
private static int ticket = 100;
@Override
public void run() {
while(true){
if(ticket>0){
System.out.println(
getName()+"当前售出第"+ticket+"张票");
ticket--;
}else{
break;
}
}
}
}
运行结果:
显然这种方法有点呆,第100张票被三个人都抢了,这就需要用锁来解决这个问题。(下节课咱们复习“锁”)。但咱们通过继承Thread重写run()方法创建的三个线程都执行成功啦!
2. 实现Runable接口
1.创建一个Runnable接口的实现类
2.在实现类中重写Runnable接口的run方法
3.创建一个Runnable接口实现类的对象
4.创建Thread类对象,构造方法中传参为:Runnable接口实现类的对象
5.调用Thread类中的start方法,启动多线程
实现Runnable接口创建多线程与继承Thread类相比优点:
- 避免了单继承的缺陷,如果一个类继承了Thread类就不能继承其他类,而实现Runnable接口后还可以继承别的类、或者实现别的接口。
- 实现Runnable接口降低了线程对象和线程任务的耦合性,把线程对象和线程任务Thread分离。
- 实现Runnable接口将线程对象单独封装,更体现面向对象思想。
public class Main {
public static void main(String[] args) {
Windows win1 = new Windows();
Windows win2 = new Windows();
new Thread(win1).start();
new Thread(win2).start();
}
}
class Windows implements Runnable{
@Override
public void run() {
for(int i = 0;i < 10;i++){
System.out.println(Thread.currentThread().getName()+" --- "+i);
}
}
}
结果如下:
三、Thread的几种方法及一些常识
1.stop()和interrupt()
区别:他们都是停止当前线程,stop方法是立即停止,不安全。interrupt则是标记这个线程可以结束了,但是怎么结束,可以在run()中通过if (isInterrupted)控制关闭建立的链接等操作后再退出,较为安全。
2.wait()
wait()是Object里面的方法,而不是Thread里面的,这一点很容易搞错。它的作用是将当前线程置于预执行队列,并在wait()所在的代码处停止,等待唤醒通知。wait()方法调用后会释放