(一)、线程的基础
1. 什么是进程?
正在运行中的应用程序QQ,通常称为进程。每个进程都有自己独立的地址空间(内存空间),每当用户启动一个进程时,操作系统就会为该进程分配一个独立的内存空间,让应用程序在这个独立的内存空间中运行。
2.什么是线程?
线程是一个轻量级的子进程,是最小的处理单元;是一个单独的执行路径。可以说:线程是进程的子集(部分)。
3.线程特点:
线程是独立的。如果在一个线程中发生异常,则不会影响其他线程。它使用共享内存区域。
4.进程和线程的区别:
1、容易创建新线程。但是,创建新进程需要重复父进程。
2线程可以控制同一进程的其他线程**。进程无法控制兄弟进程,只能控制其子进程。
3、进程拥有自己的内存空间。线程使用进程的内存空间,且要和该进程的其他线程共享这个空间;而不是在进程中给每个线程单独划分一点空间。
4、(同一进程中的)线程在共享内存空间中运行,而进程在不同的内存空间中运行。
5、线程可以使用wait(),notify(),notifyAll()等方法直接与其他线程(同一进程)通信;而,进程需要使用“进程间通信”(IPC)来与操作系统中的其他进程通信。
5.多线程和线程:
多线程是指在系统中同时运行多个线程,如果只有一个处理器只有一个进程的一个指令被执行,可以使这些线程进行交替执行,由于间隔的时间短,这些程序看上去好像在同时运行.
如果将进程划分为线程,每个线程轮流占用处理器,可以减少并发时间.
6.线程、进程、多线程总结
(1).线程就是独立的执行路径; (每个线程的路都有自己的路要走) (2).在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程, gc线程 (3).main()称之为主线程,为系统的入口,用于执行整个程序。 (4).在一个进程中,如果开辟了多个线程,线程的运行由调度器(CPU)安排调度, 调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。 (5).对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制; (6).线程会带来额外的开销,如cpu调度时间,并发控制开销。 (7).每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
(二)、线程创建 (3种)
7.Thread (线程创建)
基本方法: 1.首先要继承一个Thread 2.要重写run()方法 3.利用类调用start()开启线程
7.1Thread(获取url操作)
1.导入 commons jar包 2.调用commos jar包的copyurltofile 方法是下载指定路径的操作 FileUtils .copyURLToFile(new URL(url),new File(name)); 3.设置多线程
import org.apache.commons.io.FileUtils; import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper; import java.io.File; import java.net.URL; public class Demo1 extends Thread { private String url; private String name; private Demo1(String url, String name){ this.name=name; this.url=url; } @Override public void run() { WebDownloader webDownloader=new WebDownloader(); try { webDownloader.downloader(url,name); } catch (Exception e) { e.printStackTrace(); } System.out.println("下载名为:"+name); } //1.练习Thread,实现多线程同步下载图片 public static void main(String[] args) { Demo1 demo1=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","1.jpg"); Demo1 demo1_two=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","2.jpg"); Demo1 demo1_three=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","3.jpg"); demo1.start(); demo1_two.start(); demo1_three.start(); } } //1.下载器 class WebDownloader{ //下载方法 public void downloader(String url,String name) throws Exception{ FileUtils .copyURLToFile(new URL(url),new File(name)); } }
8.Runnable (线程创建)
1.创建一个接口 Runnable 2.创建run()接口 3.在实现类创建一个接口类的对象 4.把接口类的对象放进Thread中 5.开启start()方法
public class Demo3 implements Runnable{ @Override public void run() { for (int i = 0; i <10 ; i++) { System.out.println("run"+i); } } public static void main(String[] args) { for (int i = 0; i <10 ; i++) { System.out.println("main"+i); } //创建Runnable接口类的对象 Demo3 demo3=new Demo3(); //把类对象放进Thread中区 // Thread thread=new Thread(demo3); // thread.start(); new Thread(new Demo3()).start(); } }
9.Callable (线程创建)
1.实现callable 接口,需要返回值类型 2.重写call方法,需要抛出异常 3.创建执行服务 ExecutorService executorService= Executors.newFixedThreadPool(1); 4.提交执行: Future<Boolean> future=executorService.submit(demo5); 5.获取结果 boolean b=future.get(); 6.关闭服务 executorService.shutdown();
9.1基本语法
import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Demo5 implements Callable<Boolean> { @Override public Boolean call() { for (int i = 0; i <=1000 ; i++) { System.out.println("call "+i +"次"); } return true; } public static void main(String[] args) throws Exception{ for (int i = 0; i <=1000 ; i++) { System.out.println("main"+" "+i); } Demo5 demo5=new Demo5(); //创建执行服务 ExecutorService executorService= Executors.newFixedThreadPool(1); //提交执行: Future<Boolean> future=executorService.submit(demo5); //获取结果 boolean b=future.get(); //关闭服务 executorService.shutdown(); System.out.println(b); } }
9.2Callable(获取url)
import org.apache.commons.io.FileUtils; import org.omg.CosNaming.NamingContextExtPackage.StringNameHelper; import java.io.File; 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 Demo1 implements Callable<Boolean> { private String url; private String name; private Demo1(String url, String name){ this.name=name; this.url=url; } //1.练习Thread,实现多线程同步下载图片 public static void main(String[] args) throws Exception{ Demo1 demo1=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","1.jpg"); Demo1 demo1_two=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","2.jpg"); Demo1 demo1_three=new Demo1("https://msn-img-nos.yiyouliao.com/inforec-20221017-3b8adaf89d13eeaa1cb14bc45a596b2c.jpg?time=1666081928&signature=6771DA1973B532ECD0B6BFB9B7666680","3.jpg"); //创建执行服务 ExecutorService executorService= Executors.newFixedThreadPool(3); //提交执行: Future<Boolean> future=executorService.submit(demo1); Future<Boolean> future_one=executorService.submit(demo1_two); Future<Boolean> future_two=executorService.submit(demo1_three); //获取结果 boolean b1=future.get(); boolean b2=future_one.get(); boolean b3=future_two.get(); //关闭服务 executorService.shutdown(); } @Override public Boolean call() throws Exception { WebDownloader webDownloader=new WebDownloader(); try { webDownloader.downloader(url,name); } catch (Exception e) { e.printStackTrace(); } System.out.println("下载名为:"+name); return true; } } //1.下载器 class WebDownloader{ //下载方法 public void downloader(String url,String name) throws Exception{ FileUtils .copyURLToFile(new URL(url),new File(name)); } }
10.初识并发的问题
当多个线程,同时操作一个资源的时候,会出现数据紊乱 eg: 数量增加,抢同一张票
public class Demo4 implements Runnable{ private int ticket; public Demo4(){ticket=10;} @Override public void run() { while (true){ if (ticket<=0){ break; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"拿到了-----"+ticket--+"张票"); } } public static void main(String[] args) { Demo4 demo4=new Demo4(); new Thread(demo4,"小明").start(); new Thread(demo4,"老师").start(); new Thread(demo4,"黄牛").start(); } }
11.模拟龟兔赛跑(利用线程)
需求文档: 1.设置一个共同的赛道 (线程) 2.判断比赛是否结束 (是否到达一百步) 3.打印胜利者 4.龟兔赛跑开始 5.故事中是乌龟赢,兔子需要睡觉 6.终于,乌龟赢得了比赛
Thread.currentThread().getName() 获取当前的名字
public class Race implements Runnable{ private String Vector; @Override public void run() { for (int i = 0; i <=100; i++) { //模拟兔子休息 if (Thread.currentThread().getName().equals("兔子")&&i%10==0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } //判断比赛是否结束 boolean flag=gameOver(i); if(flag){ break; } System.out.println(Thread.currentThread().getName()+"----跑了 "+i+" 跑了多少步"); } } //判断是否成功 private boolean gameOver(int step){ if(Vector!=null){ return true; } else { // if(step>=100){ Vector=Thread.currentThread().getName(); System.out.println("成功的是:"+Vector); return true; } } return false; } public static void main(String[] args) { Race race=new Race(); new Thread(race,"兔子").start(); new Thread(race,"乌龟").start(); } }
public class Demo2 implements Runnable{ String Vector; @Override public void run() { //利用标志符号进行暂停线程 for (int i = 0; i <=100; i++) { //当兔子走的步数等于50的时候,并且当走的是兔子的时候 if(i==50&&Thread.currentThread().getName().equals("兔子")){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"走了 "+i); //得出胜利者并且结束循环的操作 if(!stop(i)){ break; } } } //判断是否成功的方法 public boolean stop(int i){ if (Vector!=null){ return false; }else { if (i>=100){ Vector=Thread.currentThread().getName(); System.out.println("成功的是:"+Vector); return false; } } return true; } public static void main(String[] args) { Demo2 demo2=new Demo2(); Thread thread=new Thread(demo2,"兔子"); Thread thread1=new Thread(demo2,"乌龟"); thread.start(); thread1.start(); } }