线程相关概念
进程
进程试试指运行中的程序,比如使用的QQ,就启动了一个程序,操作系统就会为该进程分配内存空间。当我们使用其他app就又启动了进程,操作系统就为app又分配了一个新的内存空间。
进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有自身的产生、存在和消亡的过程。
线程
线程是由进程创建的,是进程的一个实体
一个进程可以拥有多个线程
单线程和多线程
单线程:同一时刻,只运行执行一个线程。
多线程:同一时刻,可以执行多个线程。
并发和并行
并发:同一时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单理解就是单核CPU实现的多线程就是并发
如:一个CPU对应QQ和vx两个应用,那么系统在运行的时候是交替执行的,只是交替时间短
并行:同一时刻,多个任务同时执行,多核CPU可以实现并行
如:如一个CPU对应一个QQ,另外一个CPU对应VX,那么系统在运行的时候就是两个CPU同时进行
注意:多核CPU可能存在并发和并行都有的情况。
线程的基本使用
创建线程的两种方式:
1、继承Thread类,重写run方法
2、实现Runnable接口,重写run方法
继承Thread类实现线程的创建
要求:
1、编写一个程序,开启一个线程,该线程每隔1秒。在控制台输出“喵,我是小猫”
2、对上题改进:当输出10次后,结束线程
3、使用Jconsole监控线程执行情况,并画出程序示意图。
package com.li.thread;
/**
* @author 喂S别闹
* @create 2021/11/9-9:13
* @Version 1.0
* @Description:通过Thread类实现小猫题
*/
public class Thread1 {
public static void main(String[] args) throws InterruptedException {
//创建一个Cat对象
Cat cat = new Cat();
cat.start(); //最终执行cat的run方法
//cat.run() //run方法就是一个普通的方法 没有开启新的线程,就会自顶向下的执行下去,run执行完毕才继续向下执行
//当main线程启动一个子线程 Thread-0 主线程不会阻塞,会继续执行
//这是主线程与子线程是交替执行的
for (int i = 0; i < 10; i++) {
System.out.println("主线程:" + i);
Thread.sleep(1000);
}
}
//当一个类继承了Thread类,那么该类就是一个线程
//run方法 Thread类是实现了Runnable接口run方法
// @Override
// public void run() {
// if (target != null) {
// target.run();
// }
// }
// 上面的代码是Thread重写Runnable的run方法
static class Cat extends Thread {
int times = 0;
@Override
public void run() { //重写run,写自己的业务逻辑
while (true) {
System.out.println("小猫,喵喵" + (++times) + "线程名:" + Thread.currentThread().getName());
//休眠1S
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times == 10) {
break;
}
}
}
}
}
注意:start()方法和run方法:
.start(); 最终执行cat的run方法
.run() //run方法就是一个普通的方法 没有开启新的线程,就会自顶向下的执行下去,run执行完毕才继续向下执行
start()方法调用了Start0()方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态。具体什么时候执行,取决于CPU,由CPU统一调度
通过实现Runnable接口线程的创建
1、Java是单继承,在某些情况下一个类可能已经继承了某个父类,在这时用继承Thread类方法来创建线程就不可能了
2、Java设计者提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程
案例:请编写程序,该程序可以每隔1秒。在控制台输出“hi!”,当输出10次后,自动退出。请使用实现Runnable接口的方式实现. 这是静态代理模式!
public class Runnable1 {
public static void main(String[] args) {
Dog dog = new Dog();
//没有start对象
//创建了Thread对象,把dog对象实现(Runnable),放入Thread
Thread thread = new Thread(dog);
thread.start();
}
static class Dog implements Runnable {
int count = 0;
@Override
public void run() { //普通方法
while (true) {
System.out.println("hi!" + (++count) + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) {
break;
}
}
}
}
}
创建2个多线程
案例:编写一个程序,创建两个线程,一个线程每隔1秒输出“Hello World”,输出10次,退出,一个线程每隔1秒输出“Hi”输出5次后退出。
public class Thread2 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
}
static class T1 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println("Hello World" + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 8) {
break;
}
}
}
}
static class T2 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println("hi" + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 6) {
break;
}
}
}
}
}
Thread与Runnable的区别
从Java的设计来说,继承Thread或者实现Runnable接口来创建线程没有本质区别,从JDK文档知道Thread类本身就是实现了Runnable接口
实现Runnable接口方式更加合适多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable接口
案例:模拟三个售票窗口售票100,分别使用继承Thread和实现Runnable方式,并分析问题
分析:都会被脏读
会卖到102张
public class SellTicket {
public static void main(String[] args) {
// SellTicket01 sellTicket01 = new SellTicket01();
// SellTicket01 sellTicket02 = new SellTicket01();
// SellTicket01 sellTicket03 = new SellTicket01();
//出现票数超卖
// sellTicket01.start();
// sellTicket02.start();
// sellTicket03.start();
SellTicket02 sellTicket02 = new SellTicket02();
new Thread(sellTicket02).start(); //第一个线程
new Thread(sellTicket02).start(); //第2个线程
new Thread(sellTicket02).start(); //第3个线程
}
//使用Thread类
static class SellTicket01 extends Thread {
private static int Ticketnum = 100;
@Override
public void run() {
while (true) {
if (Ticketnum <= 0) {
System.out.println("售票结束了");
break;
}
//休眠
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 卖出了一张票 " + "还剩" + (--Ticketnum) + "张票");
}
}
}
//实现runnable接口
static class SellTicket02 implements Runnable {
private int Ticketnum = 100;
@Override
public void run() {
while (true) {
if (Ticketnum <= 0) {
System.out.println("售票结束了");
break;
}
//休眠
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 卖出了一张票 " + "还剩" + (--Ticketnum) + "张票");
}
}
}
}