多线程详解---(多案例实战)
Posted ꧁༺空༒白༻꧂
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程详解---(多案例实战)相关的知识,希望对你有一定的参考价值。
多线程
1、区分单线程和多线程
- 单线程:就像是做饭,洗衣服,煮水,一个一个进行
- 多线程:在单线程的基础上,可以考虑煮水的时候,洗衣服节约时间
package com.kong.thread;
//创建线程,重写run方法,start方法开启线程
public class thread1 extends Thread {
@Override
public void run() {
//run方法线程体
for (int i = 0; i <1000 ; i++) {
System.out.println("你好");
}
}
public static void main(String[] args) {
thread1 thread1=new thread1();//创建线程
thread1.start();//start开启多线程
// thread1.run();//如果是run方法,只是将run方法运行,并不是开启多线程
for (int i = 0; i <1000 ; i++) {
System.out.println(i);
}
}
}
测试结果:如果是run方法,就是按照你好输出完毕再输出i,如果是start(多线程)方法,则run方法跟i交替输出
2、网图下载
- 首先我们要知道的是多线程的调度,是CPU在操作的,而代码执行过程中,下载的网图是先后顺序的,但是如果跟第一点写的煮水洗衣服,是不是煮水会先好,多线程也是如此,就是一个争取CPU调度的过程
- 我们编写一段代码来试着探讨一下吧
package com.kong.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//1.下载图片,需要一个下载的方法
//2.编写构造器
//3.new一个下载方法
//4.编写main方法下载图片
public class thread2 extends Thread {
private String url;
private String name;
public thread2(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
DownPicture downPicture=new DownPicture();
downPicture.down(url,name);
System.out.println(name+"下载完毕");
}
public static void main(String[] args) {
thread2 t1=new thread2("http://kr.shanghai-jiuxin.com/file/2021/0609/1af55d6d59a1624ff57f5f30e19eaa4d.jpg","逢考必过1");
thread2 t2=new thread2("http://kr.shanghai-jiuxin.com/file/2021/0609/1af55d6d59a1624ff57f5f30e19eaa4d.jpg","逢考必过2");
thread2 t3=new thread2("http://kr.shanghai-jiuxin.com/file/2021/0609/1af55d6d59a1624ff57f5f30e19eaa4d.jpg","逢考必过3");
//开启多线程
t1.start();
t2.start();
t3.start();
}
}
//图片下载方法
class DownPicture{
public void down(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 运行结果不难发现,三张相同的图片运行的顺序不一样,这是因为多线程的CPU调度,每个线程都在争取运行,导致相差不多的运行时间可以变化输出
3、Runable接口
启动线程方式:传入目标对象+Thread对象.start
package com.kong.thread;
public class runable implements Runnable {
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
}
public static void main(String[] args) {
runable runable=new runable();//创建线程
new Thread(runable).start();//开启线程
for (int i = 0; i < 1000; i++) {
System.out.println("ni");
}
}
}
在Thread类里面,继承了runable接口
4、并发问题
package com.kong.thread;
//模拟购票
public class runable2 implements Runnable {
//定义一些票数
private int tickNum=10;
public void run() {
while(true){
if(tickNum<=1){
break;
}
//模拟购票操作所需时间
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到第"+tickNum--+"票");
}
}
public static void main(String[] args) {
runable2 runable2=new runable2();
new Thread(runable2,"小红").start();
new Thread(runable2,"小明").start();
new Thread(runable2,"小花").start();
}
}
注意:在模拟购票所需时间之后会出现一种一起同时购买同一张票的情况(主要原因是你还在买票时间,有人也在买这张票)
5、龟兔赛跑问题
package com.kong.thread;
public class race implements Runnable {
private String winner=null;
public void run() {
for (int i = 0; i <=100; i++) {
//模拟兔子睡觉
if(Thread.currentThread().getName()=="兔子"&&i%10==0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
boolean b = gameOver(i);
if(b){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
}
}
private boolean gameOver(int foot){
if (winner!=null) {
return true;
}else if(foot>=100){
winner=Thread.currentThread().getName();
System.out.println(winner+"获得胜利");
return true;
}
return false;
}
public static void main(String[] args) {
race race=new race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
6、静态代理模式
package com.kong.thread;
public class staticProxy {
public static void main(String[] args) {
weddingCompany wedding=new weddingCompany(new you());
wedding.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//你去实现一个结婚的方法,此处的你为真实角色
class you implements Marry{
public void HappyMarry() {
System.out.println("你要结婚了");
}
}
//结婚公司是作为代理角色
class weddingCompany implements Marry{
private Marry target;
public weddingCompany(Marry target){
this.target=target;
}
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void before(){
System.out.println("婚前准备");
}
private void after(){
System.out.println("婚后送客");
}
}
7、Lambda表达式
函数式接口的定义:
- 任何接口,如果只包含一个抽象方法,就称为函数式接口
- 对于函数式接口,我们可以通过lambda表达式创建该接口对象
package com.kong.thread;
public class lambda{
//静态内部类
// static class Like implements ILike{
// public void lambda() {
// System.out.println("你好啊");
// }
// }
public static void main(String[] args) {
//局部内部类
// class Like implements ILike{
// public void lambda() {
// System.out.println("你好啊");
// }
// }
// ILike like=new Like();
// like.lambda();
//匿名内部类,只能借助父类或者接口
// ILike like = new ILike() {
// public void lambda() {
// System.out.println("你好啊");
// }
// };
// like.lambda();
//lambda简化
ILike like=()->{
System.out.println("你好啊");
};
like.lambda();
}
}
//定义一个函数接口
interface ILike{
void lambda();
}
//实现类
//class Like implements ILike{
// public void lambda() {
// System.out.println("你好啊");
// }
//}
总结
- 就是一个继承接口之后的简化
- 只适用于函数式接口
8、线程状态
- 创建状态:new一个对象,线程对象一旦创建就进入新生状态
- 就绪状态:当用start()方法式,线程立即进入就绪状态,但不一定马上调度
- 阻塞状态:调用sleep,wait或者同步锁定,线程就会进入阻塞章台,就是代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待cpu的调度
- 执行状态:此处才是线程真正执行线程体的代码块
- 死亡状态:线程中断或者结束,都会让线程进入死亡状态,并且不可再重新启动
8.1、线程停止
- 建议线程正常停止—>利用次数,不建议死循环
- 建议使用标志位
- 不建议使用stop或者destroy
package com.kong.thread;
public class threadStop implements Runnable {
//设置一个标识位
private boolean flag=true;
@Override
public void run() {
int i=0;
while(flag){
System.out.println(i++);
}
}
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
threadStop threadStop=new threadStop();
for (int i = 0; i < 100; i++) {
System.out.println(i);
if(i==90){
threadStop.stop();
System.out.println("此线程停止");
}
}
}
}
8.2、线程休眠
- sleep(时间)指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间到达后进程回进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
package com.kong.thread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class timeSleep {
public static void main(String[] args) {
tenDown();
}
public static void tenDown() {
// //倒计时
// int num = 10;
// while (true) {
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(num--);
// if (num <= 0) {
// break;
// }
// }
// }
//打印系统当前的事件
Date date = new Date(System.currentTimeMillis());//获取当前系统时间
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date = new Date(System.currentTimeMillis());
}
}
}
作用:放大事件可能发生的概率
8.3、线程礼让
- 礼让线程,让当前正在执行的线程暂停,但是不阻塞
- 运行转为就绪
- cpu重新调度
package com.kong.thread;
public class threadYeid {
public static void main(String[] args) {
Myyied myyied = new Myyied();
new Thread(myyied,"a").start();
new Thread(myyied,"b").start();
}
}
class Myyied implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止");
}
}
结论:礼让不一定能成功
8.4、线程强制执行join(插队)
package com.kong.thread;
public class threadjoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
public static void main(String[] args) throws InterruptedException {
threadjoin threadjoin=new threadjoin();
Thread thread = new Thread(threadjoin);
thread.start();
for (int i = 0; i <50 ; i++) {
if(i==20){
thread.join();
}
System.out.println("停止");
}
}
}
8.5、线程状态观察
package com.kong.thread;
public class state {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
for (int i 多线程详解---(多案例实战)