多线程:
线程:一个进程可以有多个线程,比如app中的声音、弹幕、图像等;Thread -------------线程是不安全的
同时:一个进程至少包含一个线程,不然没有存在的意义;--------------------------------------------------------------->线程,是CPU调度和执行的单位
进程:在操作系统中运行的程序就是进程,比如app; process
程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
而进程,就是执行程序的一次"执行过程",它是一个动态的概念;--------------->进程,是系统资源分配的单位
注意:真正的多线程是指有多个cpu,即多核;
模拟的多线程:如服务器–》即在一个cpu的情况下,在同一时间点,cpu只能执行一个代码,因为切换的很快,就造成了同时执行的错觉!
线程:是独立的执行路径,在程序运行时即使没有创建线程,后台也会有多个线程,如主线程main,gc垃圾回收线程
mian,为主线程,为系统的入口,用于执行整个程序;
一个进程中,如果开辟了多个线程,则线程的运行将由调度器(CPU就是调度器)安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干扰的
对同一份资源操作时,会存在资源抢夺的问题 ---------->所以需要加入 并发 控制
线程会带来额外的开销,如cpu调度时间,并发控制开销!
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致!
mian线程–主线程–也叫 用户线程
GC线程—jvm给的—叫守护线程
线程的创建方式有三种:
第一、继承Thread类
1、自定义线程类继承Thread类
2、重写run()方法,编写方法体
3、创建线程对象,调用start()方法启动线程
//自定义
class Test1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("自定义线程,并重写run");
}
}
}
//3创建线程,调用
public static void main(String[] args) {
new Test1().start();
}
总结:线程开启不一定立即执行,由调度器CPU调度执行;
第二、实现Runnable接口
class MyThreadDemo1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("创建Runnable接口的实例");
}
}
}
public static void main(String[] args) {
//创建Runnable接口的实现类-对象;
Runnable r = new MyThreadDemo1();
//创建线程对象,通过线程对象来开启我们的线程,这里是个代理——静态代理
Thread t1 = new Thread(r);
t1.start();
}
第三、实现Callable接口(工作中可能会是 核心实现
基于第三种方式写的简单下载功能;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class CallableThread implements Callable<Boolean> {
private String url;//保存图片地址
private String file;//保存的文件名
public CallableThread(String url, String file) {
this.url=url;
this.file=file;
}
@Override
public Boolean call() {
WebDownloade2 wd = new WebDownloade2();
wd.downLoader(url,file);
System.out.println("下载了文件名为"+file);
return true;
}
public static void main(String[] args) {
CallableThread ct = new CallableThread("https://s.cn.bing.net/th?id=OHR.NorthernCaracara_EN-CN8016443142_1920x1080.jpg&rf=LaDigue_1920x1080.jpg","2.jpg");
CallableThread ct2 = new CallableThread("https://s.cn.bing.net/th?id=OHR.NorthernCaracara_EN-CN8016443142_1920x1080.jpg&rf=LaDigue_1920x1080.jpg","2.jpg");
CallableThread ct3 = new CallableThread("https://s.cn.bing.net/th?id=OHR.NorthernCaracara_EN-CN8016443142_1920x1080.jpg&rf=LaDigue_1920x1080.jpg","2.jpg");
//四步:第一步:创建执行服务、
ExecutorService ser = Executors.newFixedThreadPool(3);//nThreads--->n个线程s-->可以理解为,n线程池--准备3个
//第二步提交执行:
Future<Boolean> submit1 = ser.submit(ct);
Future<Boolean> submit2 = ser.submit(ct2);
Future<Boolean> submit3 = ser.submit(ct3);
//第三步 获取结果boolean值
try {
Boolean rs1 = submit1.get();
Boolean rs2 = submit2.get();
Boolean rs3 = submit3.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally {
//第四步:关闭服务
ser.shutdownNow();
}
}
}
//下载器
class WebDownloade2 {
//下载方法
public void downLoader(String url,String file){
//需要使用一个文件工具类,导入commons-io-2.6 ---apache的;commons-io-2.6百度下载
try {
FileUtils.copyURLToFile(new URL(url), new File(file));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现异常");
}
}
}
Thread t=new Thread();
线程常用方法:
t.currentThread();//获取主线程进行查看信息
t.currentThread().getname();//获取线程名字
t..getId();//满足条件:非空且唯一;获取线程的唯一标识
t.start();//,启动线程
t.sleep(5000);//睡眠5秒t
t.getPriority();//获取,线程的优先级; ----t.setPriority(3)----设置线程优先级-默认是5,最高max_10,最低min_1
t.isInterrupted();//当前线程是否被中断
t.isDaemon();//是否为守护线程,线程默认 都不是守护线程;-----设置true就是了,拓展实用
t.isAlive();//是否活着
t.join();//,当i==200时,,main不执行了,,让 TeJ1 来执行,插队,,很强势,少使用;
Thread.yield();//礼让一下,退出cpu使用,让别的线程先执行,详见拓展实用
Thread—需要通过继承
子类继承Thread类具备多线程能力,重写run()
启动线程:子类对象。start();
缺点:,不建议使用,局限,oop单继承,不能实现多实现;
Runnable—需要实现
实现接口Runnable具备多线程能力 重写run()
启动线程:传入目标对象+Thread对象.start();
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个子类实现(多个线程使用)
Callable----需要实现
实现接口Callable接口设定<返回值>具备多线程能力,重写call,获取<返回值>
然后使用四步:
第一:创建执行服务()
第二,提交执行
第三,获取值
第四,关闭服务,第一步对象
好处:1、可以定义返回值-------2、可以抛出异常
线程的五大状态:
创建状态:new Threa();就是
就绪状态: new Thread().start()就是
阻塞状态: new Thread().sleep(5000);睡眠5秒 就是阻塞5秒
运行状态: new Thread().start();之后—就是
死亡状态: 线程结束或者中断;
常用方法: ------------------------------特别记忆 sleep===》每个对象都有一把锁,sleep不会释放锁;
Thread t=new Thread();
t.start();//就绪
t.sleep();//睡眠 -----------------------
t.setPriority(7);//线程优先级,,--默认是5,mian就是5,设置最高是10,最低是1
t.join();//等待该线程终止,才能抢占-----》类似 插队,,必须让该线程走完才行,
t.yield();//礼让,,,多个线程抢占,,该线程调用yield后,让其他的线程先执行
t.isAlive();//测试线程是否处于活动状态