1. 进程与线程
1. 进程与线程的概述:
(1)进程:
- 进程就是正在运行的程序,是系统进行资源分配和调用的独立单位,每一个进程都有他自己的内存空间和系统资源
- 比如正在运行的应用程序(QQ,微信,QQ音乐)
(2)多进程
- 现在的计算机系统(Win,Mac,Linux)是支持多进程的,可以同时运行多个程序
- 我们的计算机在同一个时间点上,是同时执行多个进程吗?不是,我们的(单核CPU)CPU在同一个时间点上只能执行一个进程
- 你的感觉多个进程在同时执行,因为CPU在某个时间点上只能做一件事情,CPU在多个进程间进行一个高速的切换执行,你的人耳和眼睛根本就感觉不到
- 多进程的意义:多进程的作用不是提高执行速度,而是提高CPU的使用率
(3)线程
- 在进程开启后,会执行多个任务,每一个任务就称为一个线程(比如QQ音乐,听歌是一个线程,显示歌词是一个线程)
- 线程依赖于进程而存在,一个进程中至少有一个线程
- 线程是程序使用CPU的基本单位。所以,进程是拥有资源的基本单位,线程是CPU调度的基本单位
- 多线程的意义:多线程的作用不是提高执行速度,而是为了提高应用程序的使用率
2. 并发与并行
1. 并发
- 指的是多个任务,高速的交替执行
- 比如说:吃一口饭喝一口水
2. 并行
- 指的是多个任务同时执行
- 比如说:边吃饭边打电话
3. Java程序的运行原理
- Java命令会启动Java虚拟机(JVM),启动JVM等于启动了一个程序,也就是启动了一个进程
- 该进程会自动启动一个"主线程",主线程会去调用某个类中的main方法。所以main方法运行在主线程中
- JVM的启动是多线程的吗:JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的
4. 多线程的启动方式1:继承Thread类
1. Java给我们提供好了一个Thread类,通过Thread类创建线程和开启线程
2. 创建新线程的方法
- 将一个类声明为Thread的子类
- 该子类重写Thread类中的run()方法
- 分配并启动该子类的示例
3. 注意事项:
- run()方法中写的一般是比较耗时的代码
- 同一个线程在一个程序中不要重复开启,重复开启会抛出异常
public class TestDemo01 {
public static void main(String[] args) {
System.out.println("主线程的代码1");
System.out.println("主线程的代码2");
System.out.println("当主线程执行到这块的时候,开启多线程");
MyThread myThread1 = new MyThread();
myThread1.start();
System.out.println("myThread后面的代码1");
System.out.println("myThread后面的代码2");
System.out.println("在开启一条线程");
MyThread myThread2 = new MyThread();
myThread2.start();
System.out.println("最后的代码1");
System.out.println("最后的代码2");
}
}
class MyThread extends Thread{
/**
* run()方法里面的代码写的是比较耗时的代码,让子线程来进行
* 模拟耗时操作
*/
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
4. 获取和设置线程的名称
- 获取和设置线程名称的方法
public final String getName() //获取线程名称
public final void setName(String name) //设置线程名称
public MyThread(String name) {
super(name);
} //通过构造方法直接设置线程名
public static Thread currentThread() //获取当前执行的线程
- 代码示例
public class TestDemo01 {
public static void main(String[] args) {
Thread thMain = Thread.currentThread();
thMain.setName("主线程");
System.out.println(thMain.getName());
//多个线程并发执行,多个抢占CPU的执行权,哪个线程抢到,在某一个时刻,就会执行哪个线程。线程的执行具有随机性
MyThread th1 = new MyThread();
th1.setName("艾欧尼亚");
th1.start();
MyThread th2 = new MyThread();
th2.setName("诺克萨斯");
th2.start();
MyThread th3 = new MyThread();
th3.setName("战争学院");
th3.start();
}
}
class MyThread extends Thread{
//无参构造
public MyThread() {
}
//有参构造:设置线程名
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//Thread.currentThread().getName():获取当前线程的名称
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
5. 代码示例:使用多线程来复制音乐和文本文件
public class TestDemo01 {
public static void main(String[] args) {
System.out.println("主线程执行");
copyMp3Thread th1 = new copyMp3Thread();
th1.start();
copyTxtThread th2 = new copyTxtThread();
th2.start();
System.out.println("主线程后面的代码1");
System.out.println("主线程后面的代码2");
System.out.println("主线程后面的代码3");
}
}
class copyMp3Thread extends Thread{
@Override
public void run() {
//ctrl+alt+t:快速try/catch
try {
FileInputStream fis = new FileInputStream("阿刁_赵雷.mp3");
FileOutputStream fos = new FileOutputStream("newMp3.mp3");
byte[] bytes = new byte[1024 * 8];
int len = 0;
while ((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
fos.flush();
}
fis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class copyTxtThread extends Thread{
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("newAaa.txt"));
String line = null;
while ((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
6. 线程调度及设置线程优先级
(1)线程的调度模型:
- 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用的CPU时间片
- 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,则随机选取一个执行。线程优先级高的获取CPU时间会多一些
- Java使用的是抢占式调度模型
(2)获取和设置线程的优先级
public final int getPriority() //获取线程的优先级
public final void setPriority(int newPriority) //设置线程的优先级
【注意】
1.线程优先级共分成1~10,1是最低优先级(Thread.MIN_PRIORITY == 1),10是最高优先级(Thread.MAX_PRIORITY == 10)
2.线程的默认优先级是5(Thread.NORM_PRIORITY == 5)
(3)注意事项:有的时候我们给线程设置了指定的优先级,但是该线程并不是按照优先级高的线程执行,那是为什么呢?
- 因为线程的优先级的大小仅仅表示这个线程被CPU执行的概率增大了.但是我们都知道多线程具有随机性
- 所以有的时候一两次的运行说明不了问题
(4)代码示例
public class TestDemo02 {
public static void main(String[] args) {
MyThread th1 = new MyThread();
MyThread th2 = new MyThread();
MyThread th3 = new MyThread();
th1.setName("艾欧尼亚");
th2.setName("诺克萨斯");
th3.setName("战争学院");
//设置线程优先级
th1.setPriority(Thread.MIN_PRIORITY);
th2.setPriority(Thread.MAX_PRIORITY);
th3.setPriority(Thread.NORM_PRIORITY);
//获取线程优先级
System.out.println(th1.getPriority()); //1
System.out.println(th2.getPriority()); //10
System.out.println(th3.getPriority()); //5
th1.start();
th2.start();
th3.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
7. 线程控制
(1)睡眠线程:public static void sleep(long millis):设置线程睡眠,单位:毫秒
public class TestDemo01 {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开启了");
Thread.sleep(1000);
System.out.println("主线程接着执行");
MyThread th1 = new MyThread("A线程");
MyThread th2 = new MyThread("B线程");
th1.start();
th2.start();
}
}
class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
(2)加入线程:public final void join()
- 意思就是:等待该线程执行完毕了以后, 其他线程才能再次执行
- 注意事项:
在线程启动之后, 在调用方法
join ()可以让多个线程并发执行,变成串行(挨个排队执行,不用抢)
public class TestDemo01 {
public static void main(String[] args) throws InterruptedException {
MyThread th1 = new MyThread("艾欧尼亚");
MyThread th2 = new MyThread("诺克萨斯");
MyThread th3 = new MyThread("战争学院");
//注意:在线程启动之后, 在调用join()方法
th1.start();
th1.join();
th2.start();
th2.join();
th3.start();
th3.join();
/**
* 艾欧尼亚--->0
* 艾欧尼亚--->1
* 艾欧尼亚--->2
* 艾欧尼亚--->3
* 艾欧尼亚--->4
* 艾欧尼亚--->5
* 艾欧尼亚--->6
* 艾欧尼亚--->7
* 艾欧尼亚--->8
* 艾欧尼亚--->9
* 诺克萨斯--->0
* 诺克萨斯--->1
* 诺克萨斯--->2
* 诺克萨斯--->3
* 诺克萨斯--->4
* 诺克萨斯--->5
* 诺克萨斯--->6
* 诺克萨斯--->7
* 诺克萨斯--->8
* 诺克萨斯--->9
* 战争学院--->0
* 战争学院--->1
* 战争学院--->2
* 战争学院--->3
* 战争学院--->4
* 战争学院--->5
* 战争学院--->6
* 战争学院--->7
* 战争学院--->8
* 战争学院--->9
*/
}
}
class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
(3)礼让线程:public static void yield():暂停当前执行程序,并执行其他的线程【用处不大】
public class TestDemo01 {
public static void main(String[] args) {
MyThread th1 = new MyThread("艾欧尼亚");
MyThread th2 = new MyThread("诺克萨斯");
th1.start();
th2.start();
}
}
class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
Thread.yield();
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
(4)守护线程:public final void setDaemon(boolean on):将该线程标记为守护线程
- true:开启守护线程。false:关闭守护线程
- 当正在运行的线程都是守护线程时,JVM退出,守护线程就立马死掉
- 该方法必须在启动线程前调用
public class TestDemo01 {
public static void main(String[] args) {
Thread.currentThread().setName("刘备:主线程");
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
MyThread th1 = new MyThread("关羽");
MyThread th2 = new MyThread("张飞");
th1.setDaemon(true);
th2.setDaemon(true);
th1.start();
th2.start();
}
}
class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
5. 创建线程的第二种方式:Runnable接口
1. 创建线程的第二种方式
(1)定义一个类实现Runnable接口
(2)重写该接口的run()方法
(3)然后可以分配该类的实例
(4)在创建Thread对象时作为一个参数来传递并启动
2. 这种方式扩展性强,实现一个接口,还可以再去继承其他类
3. 代码示例
public class TestDemo01 {
public static void main(String[] args) {
MyRunnable1 myRunnable1 = new MyRunnable1();
Thread th1 = new Thread(myRunnable1);
th1.setName("A线程");
MyRunnable2 myRunnable2 = new MyRunnable2();
Thread th2 = new Thread(myRunnable2);
th2.setName("B线程");
th1.start();
th2.start();
}
}
class MyRunnable1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
class MyRunnable2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
6. 创建线程的第三种方式:实现Callable接口
1. 创建线程的第三种方式:
(1)创建一个类实现Callable 接口,重写接口中的call方法
(2)创建一个FutureTask类将Callable接口的子类对象作为参数传进去
(3)创建Thread类, 将FutureTask对象作为参数传进去
(4)开启线程
2. 这种创建线程的方式可以有返回值
3. 代码示例
public class TestDemo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable(100);
FutureTask<Integer> task = new FutureTask<>(myCallable);
Thread th = new Thread(task);
th.start();
//获取线程的执行结果
Integer sum = task.get();
System.out.println(sum);
}
}
class MyCallable implements Callable<Integer> {
private int num;
public MyCallable(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
//求1~num之间的和
int sum = 0;
for (int i = 1; i <= num ; i++) {
sum += i;
}
return sum;
}
}
7. 买电影票案例
1. 需求:某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票
2. 继承Thread类的方式实现
public class TestDemo01 {
public static void main(String[] args) {
SaleTicketThread th1 = new SaleTicketThread("窗口1");
SaleTicketThread th2 = new SaleTicketThread("窗口2");
SaleTicketThread th3 = new SaleTicketThread("窗口3");
th1.start();
th2.start();
th3.start();
}
}
class SaleTicketThread extends Thread{
//设置票数,把票数设置为共享变量,让三个线程来共享
static int ticket = 100;
public SaleTicketThread(String name) {
super(name);
}
@Override
public void run() {
while (true){
if (ticket >= 1){
System.out.println(Thread.currentThread().getName() + "正在出售第:" + ticket-- + "张票");
}
}
}
}
3. 实现Runnable接口的方式实现
public class TestDemo02 {
public static void main(String[] args) {
SaleTicketRunnable saleTicketRunnable = new SaleTicketRunnable();
Thread th1 = new Thread(saleTicketRunnable, "窗口1");
Thread th2 = new Thread(saleTicketRunnable, "窗口2");
Thread th3 = new Thread(saleTicketRunnable, "窗口3");
th1.start();
th2.start();
th3.start();
}
}
class SaleTicketRunnable implements Runnable{
static int ticket = 100;
@Override
public void run() {
while (true){
if (ticket >= 1){
System.out.println(Thread.currentThread().getName() + "正在出售第:" + ticket-- + "张票");
}
}
}
}