大家好,划水的三郎更新多线程了,多多指点,欢迎大家留言讨论
目录
线程概述
????????进程介绍
????????线程介绍
创建线程的三种方式
????????1.继承Thread
????????2.实现Runnable接口
????????3.实现Callable接口
线程的六种状态
????????线程执行状态流程图
????????1.NEW
????????2.RUNNABLE
????????3.BLOCKED
????????4.WAITING
????????5.TIMED_WAITING
????????6.TERMINATED
????????状态测试代码块
实现抢票功能
????????多线程带来的问题
????????解决方法
Java中文文档
线程概述
? ? ? ? 在跑步的过程之中做了些什么事情?看进程介绍和线程介绍理解多线程。
进程介绍
? ? ? ? ?进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。比如:美女去跑步了这一件事的整个过程。
线程介绍
? ? ? ? ?线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程中可以并发多个线程,每条线程并行执行不同的任务。比如:我们在跑步这个过程中同时做的事情,例如呼吸,听歌,摆POSS等等,这就是多线程。
创建线程的三种方式
1.继承Thread
? ? ? ? 重点:继承Thread类,重写run方法,run方法里面是线程体,也就是新线程的入口,调用start方法启动线程。启动方式:线程对象.start方法。
测试过程:创建StudyCSDNThread类,继承Thread类,重写run方法,创建主线程main主函数执行程序,run方法里面是一个for循环,调用start方法之后启动线程,由CPU就行调度,不归我们管了,让CPU自己去分配运行。
package com.example.demo.test;
//继承Thread类
public class StudyCSDNThread extends Thread{
//重写run方法,新线程的入口
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("文章对您有用的话,点赞关注支持一下"+(i+1));
}
}
public static void main(String[] args) {
//创建一个线程对象
StudyCSDNThread studyCSDNThread = new StudyCSDNThread();
//调用start方法开启线程
studyCSDNThread.start();
for (int i = 0; i < 5; i++) {
System.out.println("三郎学习多线程"+(i+1));
}
}
}
?运行结果如下:线程由CPU调度,每次执行结果都会不同,不必多疑。
三郎学习多线程1
文章对您有用的话,点赞关注支持一下1
文章对您有用的话,点赞关注支持一下2
文章对您有用的话,点赞关注支持一下3
三郎学习多线程2
文章对您有用的话,点赞关注支持一下4
三郎学习多线程3
文章对您有用的话,点赞关注支持一下5
三郎学习多线程4
三郎学习多线程5
总结:线程调用start方法开启之后未必执行,由CPU进行调度,看CPU心情。
2.实现Runnable接口
? ? ? ? 重点:实现Runnable接口,重写run方法,run方法里面是线程体,也就是新线程的入口,调用start方法启动线程。启动方式:new Thread(线程对象).start方法。
测试过程:创建StudyCSDNRunnable类,实现Runnable接口,重写run方法,创建主线程main主函数执行程序,run方法里面是一个for循环,只有Thread类有start方法,通过new Thread调用start方法之后启动线程,由CPU就行调度,不归我们管了,让CPU自己去分配运行。
package com.example.demo.test;
//实现Runnable接口
public class StudyCSDNRunnable implements Runnable{
//重写run方法
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("文章对您有用的话,点赞关注支持一下"+(i+1));
}
}
//主线程
public static void main(String[] args) {
//创建一个线程对象
StudyCSDNRunnable studyCSDNRunnable = new StudyCSDNRunnable();
//只有Thread才有start方法,new Thread(),放入我们的线程对象。调用start方法
new Thread(studyCSDNRunnable).start();
for (int i = 0; i < 5; i++) {
System.out.println("三郎学习多线程"+(i+1));
}
}
}
运行结果如下:跟方式一 一样,线程由CPU进行调度,每次执行接口都可能不一样。
三郎学习多线程1
文章对您有用的话,点赞关注支持一下1
三郎学习多线程2
文章对您有用的话,点赞关注支持一下2
三郎学习多线程3
文章对您有用的话,点赞关注支持一下3
三郎学习多线程4
文章对您有用的话,点赞关注支持一下4
文章对您有用的话,点赞关注支持一下5
三郎学习多线程5
总结:推荐使用Runnable,可摆脱Java单继承的局限性。线程调用start方法开启之后未必执行,由CPU进行调度,看CPU心情。
3.实现Callable接口
? ? ? ? 了解即可:实现callable接口,需要返回值,给一个返回值类型String,不写默认Object,重写call方法,会抛出一个异常 throws Exception。
?测试过程:创建StudyCSDNCallable类,实现Callable接口,重写call方法,创建主线程main主函数执行程序,创建线程池,执行线程,获取运行结果,关闭线程池。
package com.example.demo.test;
import java.util.concurrent.*;
//实现callable接口,需要返回值,给一个返回值类型String,不写默认Object
public class StudyCSDNCallable implements Callable<String> {
private String name;
private String study;
//构造方法
public StudyCSDNCallable(String name, String study) {
this.name = name;
this.study = study;
}
//重写call方法,会抛出一个异常 throws Exception
@Override
public String call() throws Exception {
//输出可以看到是多线程执行,
System.out.println(study);
//返回值
return name+study;
}
//主线程
public static void main(String[] args) throws ExecutionException,InterruptedException {
//传入两个参数,看是否是多线程执行
StudyCSDNCallable studyCSDNCallable1 = new StudyCSDNCallable("三郎===","3.1学习多线程");
StudyCSDNCallable studyCSDNCallable2 = new StudyCSDNCallable("三郎===","3.2实现callable");
//创建一个线程池,放入两个线程(线程池重点内容后续单独出一篇文章解释)
ExecutorService executorService = Executors.newFixedThreadPool(2);
//执行线程
Future<String> submit1 = executorService.submit(studyCSDNCallable1);
Future<String> submit2 = executorService.submit(studyCSDNCallable2);
//运行结果
String zyj1 = submit1.get();
//打印运行结果
System.out.println(zyj1);
String zyj2 = submit2.get();
//打印运行结果
System.out.println(zyj2);
//关闭线程池
executorService.shutdown();
}
}
运行结果:线程由CPU进行调度,每次执行接口都可能不一样。文中使用的线程数少,可以多试几次,或者多来几个线程。
3.2实现callable
3.1学习多线程
三郎===3.1学习多线程
三郎===3.2实现callable
总结:非重点,此了解即可。
线程的六种状态
很多博客上都是五种状态,但其实是六种,初始状态,运行状态,也称就绪状态,阻塞状态,等待状态,等待状态分为两种,等待指定时间状态和等待状态,还有终止状态。摘自翻译的Java中文官方文档,文档在文章最后可下载,由下载链接。
线程执行状态流程图
? ? ? ? 图文详解
1.NEW
? ? ? ? 初始状态:尚未启动的线程的线程状态。
2.RUNNABLE
? ? ? ? 运行状态(就绪状态):一个可运行的线程的线程状态。 由CPU决定,CPU已经调度的话是运行状态,就绪状态就是CPU还没有进行调度,已经做好被调度的准备。
3.BLOCKED
? ? ? ? 阻塞状态:线程阻塞等待监视器锁的线程状态。?
4.WAITING
? ? ? ? 等待状态:等待线程的线程状态。?
5.TIMED_WAITING
? ? ? ? 等待指定时间状态:具有指定等待时间的等待线程的线程状态。?
6.TERMINATED
? ? ? ?终止状态: 终止线程的线程状态。 线程终止不可再次开启。
状态测试代码块
package com.example.demo.test;
public class StudyCSDNState implements Runnable{
public synchronized static void main(String[] args) throws InterruptedException {
System.out.println("###---start---###");
System.out.println("===线程的创建-运行-终止===");
//创建线程对象
StudyCSDNState studyCSDNState = new StudyCSDNState();
//创建Thread方法
Thread thread = new Thread(studyCSDNState);
System.out.println("===没有调用start方法前,当前线程的状态"+thread.getState());
//调用start方法
thread.start();
System.out.println("===调用start后线程状态"+thread.getState());
Thread.sleep(100);
System.out.println("===线程进入等待状态"+thread.getState());
Thread.sleep(2000);
System.out.println("===等待两秒,查看线程状态"+thread.getState());
System.out.println("###---end---###");
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果如下:
###---start---###
===线程的创建-运行-终止===
===没有调用start方法前,当前线程的状态NEW
===调用start后线程状态RUNNABLE
===线程进入等待状态TIMED_WAITING
===等待两秒,查看线程状态TERMINATED
###---end---###
实现抢票功能
????????一共有10张车票,创建了三个不同的对象来抢购。
多线程带来的问题
当多个线程操作同一对象的时候,就会有抢错,负数,抢到同一张等的情况发生,下面代码是抢错的代码,可试验一下。
package com.example.demo.test;
/**
*多个线程操作同一个对象
*买火车票为例子
*
* 发现问题:多个线程操作同一个对象线程不安全了,数据混乱
*
*/
public class StudyRunnable2 implements Runnable{
//10张车票
private Integer fare = 10;
private Boolean change = true;
@Override
public void run() {
while (change){
try {
zyj();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void zyj() throws InterruptedException {
if(fare<=0){
change = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"----->拿到了第"+fare--+"票");
}
public static void main(String[] args) {
//单线程执行没有问题,多线程执行出现问题
StudyRunnable2 studyRunnable2 = new StudyRunnable2();
new Thread(studyRunnable2,"张三").start();
new Thread(studyRunnable2,"李四").start();
new Thread(studyRunnable2,"王五").start();
}
}
解决方法
使用synchronized关键字修饰,由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,?内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
修改上边的代码,找到zyj()方法,在此方法加上synchronized即可,修改的代码如下
public synchronized void zyj() throws InterruptedException {
if(fare<=0){
change = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"----->拿到了第"+fare--+"票");
}
运行结果:运行出来若都是一个人拿到的话,多运行几次,这个CPU决定的。
张三----->拿到了第10票
张三----->拿到了第9票
张三----->拿到了第8票
张三----->拿到了第7票
张三----->拿到了第6票
张三----->拿到了第5票
张三----->拿到了第4票
李四----->拿到了第3票
李四----->拿到了第2票
王五----->拿到了第1票
?总结:使用synchronized会影响效率,不使用会导致数据混乱,在数据安全方面,还是舍弃效率吧,synchronized最好加在修改数据的方法上,可以少影响点效率。
注:产品改需求了............此处省略一万字.........得写代码了,线程池等后续单独出一篇。
Java中文文档
百度网盘链接:https://pan.baidu.com/s/124_gXmqRs5Ng8LUW_Buq8A? 提取码:i6i8
|