Process与Thread:
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念,而进程则是执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位,通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。
线程是CPU调度和执行的的单位
注意:
很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器,如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局.
线程就是独立的执行路径:
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;main()称之为主线程,为系统的入口,用于执行整个程序;
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度
,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;线程会带来额外的开销,如cpu调度时间,并发控制开销。
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
Thread:
线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。
注:线程不一定立即执行,CPU安排调度
创建线程的方式1:继承Thread类
举例:
public class Test_Thread extends Thread{//1:自定义线程类继承Thread类
@Override
public void run() { //2:重写run()方法体部分
for(int i=0;i<20;i++){
System.out.println("我在看代码"+i);
}
}
public static void main(String[]args){
Test_Thread test_thread=new Test_Thread()://创建线程对象
test_thread.start();//3:调用start()方法,开启线程
for(int i=0;i<20;i++){
System.out.println("我在学习多线程"+i);
}
}
}
test_thread.start();
//调用start()方法,开启线程输出如下:
我在学习多线程0
我在学习多线程1
我在看代码0
我在学习多线程2
我在看代码1
我在学习多线程3
我在学习多线程4
我在学习多线程5
我在看代码2
我在学习多线程6
我在看代码3
我在学习多线程7
我在学习多线程8
我在学习多线程9
我在看代码4
我在学习多线程10
我在学习多线程11
我在看代码5
我在学习多线程12
我在看代码6
我在学习多线程13
我在看代码7
我在学习多线程14
我在学习多线程15
我在看代码8
我在学习多线程16
我在看代码9
我在学习多线程17
我在看代码10
我在学习多线程18
我在看代码11
我在学习多线程19
我在看代码12
我在看代码13
我在看代码14
我在看代码15
我在看代码16
我在看代码17
我在看代码18
我在看代码19
test_thread.run();
调用普通方法输出如下:
我在看代码0
我在看代码1
我在看代码2
我在看代码3
我在看代码4
我在看代码5
我在看代码6
我在看代码7
我在看代码8
我在看代码9
我在看代码10
我在看代码11
我在看代码12
我在看代码13
我在看代码14
我在看代码15
我在看代码16
我在看代码17
我在看代码18
我在看代码19
我在学习多线程0
我在学习多线程1
我在学习多线程2
我在学习多线程3
我在学习多线程4
我在学习多线程5
我在学习多线程6
我在学习多线程7
我在学习多线程8
我在学习多线程9
我在学习多线程10
我在学习多线程11
我在学习多线程12
我在学习多线程13
我在学习多线程14
我在学习多线程15
我在学习多线程16
我在学习多线程17
我在学习多线程18
我在学习多线程19
以上两种方法的输出结果不同的原因如下:
普通方法调用和多线程:
一个进程可以有多个线程,如视频中同时听声音,看图像,看弹幕,等等
在操作系统中,运行的程序就是进程,比如你的播放器,游戏,IDE等等
实现同步下裁图片:
首先需要在官网下载commons-io-2.6.jar
:传送门
右击:
举例:
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//准备下载器
class webdownload {
//下载方法
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
public class Thread_test extends Thread{
private String url;
private String name;
public Thread_test(String url,String name){
this.url=url;
this.name=name;
}
@Override
public void run() {
webdownload webdownload=new webdownload();
webdownload.downloader(url,name);
System.out.println("下载成功"+name);
}
public static void main(String[] args) {//进行下载
Thread_test thread_test1=new Thread_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548","name1");
Thread_test thread_test2=new Thread_test("https://img1.baidu.com/it/u=1605341541,1182642759&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=a82bfa5ed4d6a73504bb53932cfefe3f","name2");
Thread_test thread_test3=new Thread_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548","name3") ;
thread_test1.start();
thread_test2.start();
thread_test3.start();
}
}
是通过多线程同步下载这三张图片,因此,每次下载的成功的先后顺序是不相同的,具体由CPU决定:
创建线程的方式2:实现Runnable:
注:推荐使用Runnable对象,因为Java单继承的局限性
举例:
public class Runnable_test implements Runnable {//实现Runnable接口
@Override
public void run() {//实现run()方法,编写线程执行体
for(int i=0;i<20;i++){
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
Runnable_test runnable_test=new Runnable_test();
new Thread(runnable_test).start();//创建线程对象,调用start()方法启动线程
for(int i=0;i<20;i++)
{
System.out.println("我在练习多线程"+i);
}
}
}
输出:
我在看代码0
我在看代码1
我在学习多线程0
我在看代码2
我在学习多线程1
我在看代码3
我在学习多线程2
我在看代码4
我在看代码5
我在学习多线程3
我在看代码6
我在学习多线程4
我在看代码7
我在学习多线程5
我在看代码8
我在学习多线程6
我在看代码9
我在学习多线程7
我在看代码10
我在学习多线程8
我在看代码11
我在看代码12
我在看代码13
我在学习多线程9
我在看代码14
我在看代码15
我在看代码16
我在看代码17
我在看代码18
我在看代码19
实现同步下载图片:
与上文中的Thread相比,只需要修改下述这两个地方:
从继承Thread变为实现Runnable接口:
加粗样式需要单独创建线程对象,去调用start()方法:
继承Thread类与实现Runnable接口对比:
子类继承Thread类具备多线程能力
启动线程:子类对象start()
不建议使用: 避免OOP单继承局限性
实现接口Runnable具有多线程能力
启动线程:传入目标对象+Thread对象start()
推荐使用: 避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
多个线程操作同一个资源的情况:
线程不安全,数据紊乱
public class Runnable_test implements Runnable {//实现Runnable接口
private int ticknumbers=10;
@Override
public void run() {
while(true){
if(ticknumbers<=0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticknumbers--+"票");//currentThread()监测线程的状态
}
}
public static void main(String[] args) {
Runnable_test runnable_test=new Runnable_test();
new Thread(runnable_test,"小明").start();
new Thread(runnable_test,"小黄").start();
new Thread(runnable_test,"小红").start();
}
}
在输出的数据中,显然出现了,一张票同时被大于1人拿到的情况,这与我们的现实显然不相符合
模拟"龟兔赛跑":
public class Race_test implements Runnable {
public static void main(String[] args) {
Race_test race_test=new Race_test();
//创建线程对象
new Thread(race_test,"乌龟").start();
new Thread(race_test,"兔子").start();
}
private static String winner;
@Override
public void run() {
for(int i=0;i<=50;i++){//让兔子"每隔一段时间休息一下"
if(Thread.currentThread().getName().equals("兔子")&&i%5==0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
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>=50) {//判出胜负,比赛结束
winner=Thread.currentThread().getName();
System.out.println("winner is"+winner);
return true;
}
}
return false;
}
}
输出:
乌龟---->跑了0步
兔子---->跑了0步
乌龟---->跑了1步
兔子---->跑了1步
乌龟---->跑了2步
兔子---->跑了2步
乌龟---->跑了3步
兔子---->跑了3步
乌龟---->跑了4步
兔子---->跑了4步
乌龟---->跑了5步
乌龟---->跑了6步
乌龟---->跑了7步
乌龟---->跑了8步
乌龟---->跑了9步
乌龟---->跑了10步
乌龟---->跑了11步
乌龟---->跑了12步
乌龟---->跑了13步
乌龟---->跑了14步
乌龟---->跑了15步
乌龟---->跑了16步
乌龟---->跑了17步
乌龟---->跑了18步
乌龟---->跑了19步
乌龟---->跑了20步
乌龟---->跑了21步
乌龟---->跑了22步
乌龟---->跑了23步
乌龟---->跑了24步
乌龟---->跑了25步
乌龟---->跑了26步
乌龟---->跑了27步
乌龟---->跑了28步
乌龟---->跑了29步
乌龟---->跑了30步
乌龟---->跑了31步
乌龟---->跑了32步
乌龟---->跑了33步
乌龟---->跑了34步
乌龟---->跑了35步
乌龟---->跑了36步
乌龟---->跑了37步
乌龟---->跑了38步
乌龟---->跑了39步
乌龟---->跑了40步
乌龟---->跑了41步
乌龟---->跑了42步
乌龟---->跑了43步
乌龟---->跑了44步
乌龟---->跑了45步
乌龟---->跑了46步
乌龟---->跑了47步
兔子---->跑了5步
乌龟---->跑了48步
兔子---->跑了6步
乌龟---->跑了49步
兔子---->跑了7步
Winner is乌龟
创建线程的方式3:实现Callable接口 :
1.实现Callable接口,需要返回值类型----Boolean[注意是大写开头]
2.重写call方法,需要抛出异常
3.创建目标对象
//以下三步为Callable接口的固定步骤
4.创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1):
5.提交执行: Future<Boolean> result1 = ser.submit(t1);
6.获取结果: boolean r1 = result1.get()
7.关闭服务: ser.shutdownNow();
举例:
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Callable_test implements Callable<Boolean> {
public static void main(String[] args) {
Callable_test callable_test1 = new Callable_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548", "梅花1");
Callable_test callable_test3 = new Callable_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548", "梅花3");
Callable_test callable_test2 = new Callable_test("https://img2.baidu.com/it/u=1395980100,2999837177&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1671987600&t=dc660191a7f7ef18d080deaffe75d548", "梅花2");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1=ser.submit(callable_test1);
Future<Boolean> r2= ser.submit(callable_test2);
Future<Boolean>r3= ser.submit(callable_test3);
//获取结果
boolean rs = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务
ser.shutdownNow();
}
private String url;
private String name;
public Callable_test(String url,String name) {
this.url=url;
this.name=name;
}
@Override
public Boolean call(){
webdownload webdownload=new webdownload();
webdownload.downloader(url,name);
System.out.println("文件下载成功!");
return true;
}
}
//下载器
class webdownload {
//下载方法
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}