文章目录
01.线程基本概念
程序、进程(Process)、线程(Thread)概念
1.程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
2.进程:程序的一次执行过程,是一个动态的概念。是系统资源分配的单位。
3.线程:一个进程可以包含若干个线程,一个进程中至少有一个线程,否则进程没有存在的意义。线程是CPU调度和执行的单位。
02.线程创建方式
1.1 继承Thread类(重点)
- (1)自定义线程类继承Thread类
- (2)重写run()方法
- (3)创建线程对象,调用start()方法启动线程
public class Threadone extends Thread{//1.继承Thread类
@Override
public void run() {//2.重写run()方法
//run()方法线程体
for (int i = 1; i <=10; i++) {
System.out.println("多线程学习!");
}
}
public static void main(String[] args) {//主线程,main()线程
//创建一个线程对象
Threadone thread1 = new Threadone();//3.创建线程对象,调用start()方法启动线程
//线程对象调用start()方法开启线程
thread1.start();
for (int i = 1; i <=1000; i++) {
System.out.println("主线程内容");
}
}
}
结果截图(两个线程交替使用CPU)
1.2 实现Runable接口(重点)
- (1)自定义类实现Runnable接口
- (2)实现run()方法
- (3)创建线程对象,调用start()方法启动线程
public class Threadtwo implements Runnable{
@Override
public void run() {//重写run()方法
for (int i = 0; i < 10; i++) {
System.out.println("重写run()方法");
}
}
public static void main(String[] args) {
//创建Runnable接口的实现类对象、
Threadtwo threadtwo = new Threadtwo();
//创建线程对象,通过线程对象来开启线程(代理)
Thread thread = new Thread(threadtwo);
thread.start();
for (int i = 1; i <=1000; i++) {
System.out.println("主线程内容!");
}
}
}
结果截图(两个线程交替使用CPU)
1.3 实现Callable接口(自行了解)
1.4 小总结
继承Thread类:
1.子类继承Thread类具备多线程能力
2.启动线程:子类对象.start()
3.不建议使用:避免OOP单继承局限性
实现Runnable接口:
1.实现接口Runnable具有多线程能力
2.启动线程:传入目标对象+Thread.start()
3.推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
03.多线程模拟龟兔赛跑
- 首先来个赛道距离,然后离终点距离越来越近
- 判断比赛是否结束
- 打印出胜利者
- 龟兔赛跑开始
- 故事中乌龟赢的比赛,要模拟兔子睡觉
- 最终,乌龟赢的比赛
package ThreadLearn;
public class Race implements Runnable{
//胜利者
private String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉,
if(Thread.currentThread().getName().equals("兔子")&&i%10==0){//获取线程名称
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if(flag){
break;
}
System.out.println(Thread.currentThread().getName()+"向前跑了:"+i+"步");
}
}
private boolean gameOver(int steps){
//判断是否完成比赛
if(winner!=null){//若以及存在胜利者
return true;
}else {
if (steps>=100){
winner = Thread.currentThread().getName();
System.out.println("胜利者是---"+winner);
return true;
}
return false;
}
}
public static void main(String[] args) {
Race race = new Race();
Thread runner1 = new Thread(race,"小兔子");
Thread runner2 = new Thread(race,"小乌龟");
runner1.start();
runner2.start();
}
}
结果截图
04.Lambda表达式
1.为避免内部类过多
(params)->expression[表达式]
(params)->statement[语句]
(params)->{statement}
a->System.out.println("Welcome to Java");
2.函数式接口:只包含唯一一个抽象方法的接口
public class TestLamda1 {
//3.静态内部类
static class Like2 implements ILike{
@Override
public void lamda() {
System.out.println("I don't like Lambda");
}
}
public static void main(String[] args) {
ILike like = new Like1();
like.lamda();
like = new Like2();
like.lamda();
//4.局部内部类
class Like3 implements ILike{
@Override
public void lamda() {
System.out.println("I don't like Lambda,too");
}
}
like = new Like3();
like.lamda();
//5.匿名内部类,设有类的名称,必须借助类的接口或者父类
like = new ILike(){
@Override
public void lamda() {
System.out.println("I don't like Lambda,too much");
}
};
like.lamda();
//6.用Lambda简化(由匿名内部类简化)
like = ()->{System.out.println("Studying Lambda,now");};
like.lamda();
}
}
//1.定义一个函数式接口(即:一个方法只包含一个抽象方法)
interface ILike{
void lamda();
}
//2.实现类
class Like1 implements ILike{
@Override
public void lamda() {
System.out.println("I like Lambda");
}
}
结果截图
05.线程状态
状态简介1:
状态简介2:
线程方法
方法 | 说明 |
---|---|
setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程体休眠 |
void join() | 等待线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程(最好别用) |
boolean isAlive() | 测试线程是否处于活动状态 |
5.1 线程停止_stop
//测试stop类
//1.建议线程正常停止----利用次数,不建议死循环
//2.建议使用标志位----设置一个标志位
//3.不要使用stop和destory等过时方法
public class TestStop implements Runnable{
//1.设置一个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("开启线程Run--***--Thread "+i++);
}
}
//2.设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop test1 = new TestStop();
Thread thread1 = new Thread(test1);
thread1.start();
//主线程
for (int i = 0; i < 1000; i++) {
System.out.println("主线程mian执行"+i+"次");
if(i==900){
//调用stop方法切换标志,让线程停止
test1.stop();
System.out.println("线程该停止了");
}
}
}
}
5.2 线程休眠_sleep
1.sleep(毫秒)指定当前线程阻塞的毫秒数
2.sleep存在异常InteruptedException
3.sleep时间到达后线程进入就绪状态
4.sleep可以模拟网络延时
5.每个对象都有一个锁,sleep不会释放锁
//测试sleep
//模拟网络延时
public class TestSleep implements Runnable{
//票数
private int ticketNums = 10;
@Override
public void run() {
while (true){
if(ticketNums<=0){
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买了第"+ticketNums--+"张票");
}
}
public static void main(String[] args) {
TestSleep ticket = new TestSleep();
new Thread(ticket,"景天").start();
new Thread(ticket,"雪见").start();
new Thread(ticket,"龙葵").start();
}
}
//有个问题:同张票被两个人(线程)买了
sleep模拟倒计时
import java.text.SimpleDateFormat;
import java.util.Date;
//模拟倒计时
public class TestSleep2 {
public static void main(String[] args) {
//获取当前系统时间
Date startTime = new Date(System.currentTimeMillis());
while (true){
countdown();
}
}
//模拟倒计时
public static void countdown(){
int num = 10;
while (true){
try {
Thread.sleep(1000);
System.out.println(num--);
if(num<=0){
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5.3 线程礼让_yield
1.线程礼让,让当前正在执行的线程暂停,但不阻塞(即一同排队等待CPU)
2.让线程从运行状态转为就绪状态
3.让CPU重新调度,礼让不一定成功,看CPU心情 O(∩_∩)O
public class TestYield {
public static void main(String[] args) {
Yield yield = new Yield();
new Thread(yield,"线程1").start();
new Thread(yield,"线程2").start();
}
}
class Yield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName()+"线程执行结束");
}
}
礼让成功
礼让失败
5.4 线程强制执行_join
1.Join合并线程,待此线程执行完后,在执行其他线程,其他线程阻塞
2.可以想象为插队
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("VIP优先级高优先处理 "+i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
TestJoin test = new TestJoin();
Thread thread = new Thread(test);
thread.start();
//主线程
for (int i = 0; i < 100; i++) {
if(i==20) {
thread.join();
}
System.out.println("主线程"+i);
}
}
}
在 i==20时,VIP插队
06.观测线程状态
线程状态:
1.NEW 尚未启动的线程处于此状态
2.RUNNABLE 在JAVA虚拟机中执行的线程处于此状态
3.BLOCKED 被阻塞等待监视锁定的线程处于此状态
4.WAITING 正在等待另一个线程执行特定动作线程处于此状态
5.TIME_WAITING 正在等待另一个线程执行特定动作达到指定等待时间的线程处于此状态
6.TERMINATED 已退出的线程处于此状态
public class TestState extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("*******");
}
public static void main(String[] args) {
Thread thread = new Thread();
//观察状态
Thread.State state = thread.getState();
System.out.println("线程启动前:"+state);
//线程启动后
thread.start();
state = thread.getState();
System.out.println("线程启动后:"+state);
}
}
截图显示
07.线程优先级
1.Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器
按照优先级决定应该调度哪个线程来执行。
2.线程的优先级用数字表示,范围由1~10,优先级由低到高
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
3.使用以下方式改变或获取优先级
getPriority();
setPriority(int xxx);
public class TestPriority {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"线程的优先级为: "+Thread.currentThread().getPriority());
IPriority priority = new IPriority();
Thread t0 = new Thread(priority);
Thread t1 = new Thread(priority);
Thread t2 = new Thread(priority);
Thread t3 = new Thread(priority);
//先设置优先级,再启动线程
t0.start();
t1.setPriority(1);
t1.start();
t2.setPriority(4);
t2.start();
t3.setPriority(Thread.MAX_PRIORITY);
t3.start();
}
}
class IPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程的优先级为: "+Thread.currentThread().getPriority());
}
}
结果截图
08.守护线程
1.线程分为用户线程和守护线程
2.虚拟机必须确保用户线程执行完毕
3.虚拟机不用等待守护线程执行完毕
package ThreadLearn.ThreadState;
/*
1.线程分为用户线程和守护线程
2.虚拟机必须确保用户线程执行完毕
3.虚拟机不用等待守护线程执行完毕
*/
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
Human human = new Human();
Thread thread = new Thread(god);//默认false表示用户线程,正常线程都是用户线程
thread.setDaemon(true);
thread.start();//上帝守护线程启动
new Thread(human).start();//用户线程
}
}
//上帝
class God implements Runnable{
private boolean flag = true;
@Override
public void run() {
while(true) {
System.out.println("上帝守护人类!!!");
}
}
}
//人类
class Human implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("人类开开心心的活着!!!");
}
System.out.println("再见!================================地球");
}
}
守护线程始终结束在用户线程之后
09. 线程同步
多个线程操作同一个资源
并发:同一个对象被多个线程同时操作
每一个对象都有一个锁
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候就需要线程同步。
线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,
等待前面线程使用完毕,下一个线程再使用.
锁机制 synchronized : 当一个线程获得对象的排他锁,独占资源,其他线程必须等待使用后释放解锁即可。当同时也存在以下问题:
学习视频连接:狂神多线程详解