多线程
程序、进程、线程
在操作系统中运行的程序就是进程,比如QQ、播放器、游戏、IDE等等
一个进程可以有多个线程,如视频中同时听声音,看图像,看弹幕,等等
Process与Thread
说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。
通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的的单位。
注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。
线程
多线程
概念:
线程就是独立的执行路径;
在程序运行时,即使没有自己创建线程,后台也会有多个线程。如主线程、gc线程等;
main()称之为主线程,为系统的入口,用于执行整个程序;
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的;
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
线程会带来额外的开销,如cpu调度时间,并发控制开销;
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
实现多线程的三种方法:
1、继承Thread类 重写run方法 使用start开启线程
public class student extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我在玩手机");
}
}
}
public class Application {
public static void main(String[] args) {
student student = new student();
student.start();
for (int i = 0; i < 200; i++) {
System.out.println("我在玩看电视呢");
}
}}
多线程下载图片
package oop;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class webdownloader extends Thread{
private String url;
private String file;
public void webdownloader(String url,String file) {
this.url=url;
this.file=file;
}
@Override
public void run() {
loader loader = new loader();
loader.loader(url,file);
System.out.println("下载了"+file);
}
}
class loader{
public void loader(String url,String file) {
try {
FileUtils.copyURLToFile(new URL(url),new File(file));
} catch (IOException e) {
System.out.println("出现异常啦");
throw new RuntimeException(e);
}
}
}
package oop;
import static java.lang.Math.random;
public class Application {
public static void main(String[] args) {
webdownloader t1 = new webdownloader();
webdownloader t2 = new webdownloader();
webdownloader t3 = new webdownloader();
t1.webdownloader("https://i0.hdslb.com/bfs/banner/39bb071c9afff5fbb461107562a82b51a4b8e859.jpg@976w_550h_1c.webp","p1.jpg");
t2.webdownloader("https://i0.hdslb.com/bfs/banner/5bce62f82a1def76993fd16fe058477c68f41021.jpg@976w_550h_1c.webp","p2.jpg");
t3.webdownloader("https://i0.hdslb.com/bfs/archive/95fdcdf38b3d557c1ce9463cbae49f5bbafc0fd9.jpg@672w_378h_1c.webp","p3.jpg");
t1.start();
t2.start();
t3.start();
}
}
输出:
下载了p3.jpg
下载了p1.jpg
下载了p2.jpg
实现Runnable接口,重写run方法,将实现的对象传递给Thread中
package oop;
import static java.lang.Math.random;
public class Application {
public static void main(String[] args) {
webdownloader t1 = new webdownloader();
webdownloader t2 = new webdownloader();
webdownloader t3 = new webdownloader();
t1.webdownloader("https://i0.hdslb.com/bfs/banner/39bb071c9afff5fbb461107562a82b51a4b8e859.jpg@976w_550h_1c.webp","p1.jpg");
t2.webdownloader("https://i0.hdslb.com/bfs/banner/5bce62f82a1def76993fd16fe058477c68f41021.jpg@976w_550h_1c.webp","p2.jpg");
t3.webdownloader("https://i0.hdslb.com/bfs/archive/95fdcdf38b3d557c1ce9463cbae49f5bbafc0fd9.jpg@672w_378h_1c.webp","p3.jpg");
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}}
小结:推荐使用runnable接口,可以避免单继承的局限性
lambdar表达式
限制:接口中只能有一个约束的方法。
用途:当我们需要一个方法体仅仅使用一次的时候,可以大大简化代码量
public interface testInter {
public abstract void fun1();
}
testInter inter = null;
inter = () -> {
System.out.println("有意思");
};
inter.fun1();
线程停止
线程需要手动的释放掉,但是不建议用thread中自带的stop方法,但是可以自己手动停止
package oop;
import static java.lang.Math.random;
public class Application {
public static void main(String[] args) {
Thread2 thread = new Thread2();
new Thread(thread).start();
for (int i = 0; i < 200; i++) {
System.out.println("也在runnnnnnnn"+i);
if (i==60)thread.stop();
}
}}
package oop;
public class Thread2 implements Runnable{
boolean flag = true;
@Override
public void run() {
for (int i = 0; i < 200; i++) {
if (flag){System.out.println("runnnnnnnnnnThread"+i);}
else break;
}
}
public void stop(){
flag =false;
}
}
线程礼让
礼让线程,让当前正在执行的线程暂停,但不阻塞
将线程从运行状态转为就绪状态
让cpu重新调度,礼让不一定成功!要看cpu心情!
守护线程
package oop;
public class testpriority {
public static void main(String[] args) {
Thread god = new Thread(() -> {
while (true) System.out.println("上帝会一直保佑你");
});
Thread you = new Thread(() -> {
for (int i = 0; i <= 36500; i++) {
System.out.println("每天都很开心的活着");
if (i==36500) System.out.println("bye world~");
}
});
god.setDaemon(true); // 默认的线程是false修改为true之后就变成了守护线程
god.start();
you.start();
}
}
线程锁
synchronized(object){} 注意:object是可以变的属性
自己带锁的数组CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArrayList;
public class list {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList le = new CopyOnWriteArrayList();
for (int i = 0; i < 10000; i++) {
new Thread(()-> {
le.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(200);
System.out.println(le.size());
}
死锁
每个对象都有一把自己的锁,当使用两个线程相互要使用对象对方对象的时候,这就会造成二者陷入矛盾中,这就是死锁,而解决死锁的方法也很简单,就是将两个对象独立出来
线程池
package Threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class pool {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new test());
service.execute(new test());
service.execute(new test());
service.execute(new test());
service.shutdown();
}
}
class test implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)