多线程详解

0.多线程重点:线程实现  线程同步

1.线程简介

多任务:同时做多个事情,看起来是多个任务都在做,其实本质上我们的大脑在同一时间内只做一件事。

多线程例子:一条车道,车太多,会造成道路堵塞,效率极低,为了提高效率,充分利用道路,于是加了多个车道。

生活中的多线程例子:打游戏与朋友开黑

普通方法调用和多线程的区别

程序,进程,线程

进程:在操作系统中运行的程序就是进程,比如QQ,游戏,IDEA

一个进程可以有多个线程,如视频中同时可以听到声音,看到图像和弹幕等等

程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

进程(Process):进程是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。

通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。

注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器。

   如果是模拟出来的多线程,即在一个CPU的情况下,在同一时间点,CPU只能执行一个代码,因为切换的很快,所有就有同事执行的错觉。

线程核心概念

  • 线程是独立的执行路径
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,GC线程
  • main()称之为主线程,为系统的入口,用于执行整个程序
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器(CPU)安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
  • 线程会带来额外的开销,如CPU调度时间,并发控制开销
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

2.线程创建

线程创建的三种方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

1.继承Thread

  • 自定义线程类继承Thread
  • 重写run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
 1 /*
 2 * 创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
 3 * */
 4 public class TestThread1 extends Thread{
 5     @Override
 6     public void run() {
 7         //run方法线程体
 8         for (int i = 0; i < 200; i++) {
 9             System.out.println("我在看代码--"+i);
10         }
11     }
12 
13     public static void main(String[] args) {
14         //main线程  主线程
15         //创建一个线程对象
16         TestThread1 testThread1 = new TestThread1();
17         //调用start()方法开启线程
18         testThread1.start();
19         for (int i = 0; i < 1000; i++) {
20             System.out.println("我在学习多线程"+i);
21         }
22     }
23 }

说明两个线程是同时执行的

线程不一定立即执行,CPU安排调度

案例:下载图片

下载coomms.io包

 

创建lib包目录,将commons-io复制到包中,并添加为库

 

 

 编写代码

 1 import org.apache.commons.io.FileUtils;
 2 
 3 import java.io.File;
 4 import java.io.IOException;
 5 import java.net.URL;
 6 
 7 public class TestThread2 extends Thread{
 8     private String url;
 9     private String name;
10 
11     public TestThread2(String url, String name) {
12         this.url=url;
13         this.name=name;
14     }
15 
16     @Override
17     public void run() {
18         WebDownloader webDownloader = new WebDownloader();
19         webDownloader.downloader(url,name);
20         System.out.println("下载了文件名为"+name);
21     }
22 
23     public static void main(String[] args) {
24         TestThread2 t1 = new TestThread2("https://m.360buyimg.com/babel/jfs/t1/121907/24/12292/426871/5f5a3a79E1e683de4/7dc3d2aa34e709ff.gif","t1");
25         TestThread2 t2 = new TestThread2("https://m.360buyimg.com/babel/jfs/t1/121907/24/12292/426871/5f5a3a79E1e683de4/7dc3d2aa34e709ff.gif","t2");
26         TestThread2 t3 = new TestThread2("https://m.360buyimg.com/babel/jfs/t1/121907/24/12292/426871/5f5a3a79E1e683de4/7dc3d2aa34e709ff.gif","t3");
27         TestThread2 t4 = new TestThread2("https://m.360buyimg.com/babel/jfs/t1/121907/24/12292/426871/5f5a3a79E1e683de4/7dc3d2aa34e709ff.gif","t4");
28 
29         t1.start();
30         t2.start();
31         t3.start();
32         t4.start();
33     }
34 }
35 //下载器
36 class WebDownloader{
37     //下载方法
38     public void downloader(String url,String name){
39         try {
40             FileUtils.copyURLToFile(new URL(url),new File(name));
41         } catch (IOException e) {
42             e.printStackTrace();
43             System.out.println("IO异常,downloader方法出现问题");
44         }
45     }
46 }

结果显示

 

 2.实现Runnable

  • 定义TestRunnable类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
 1 public class TestRunnable implements Runnable{
 2     @Override
 3     public void run() {
 4         for (int i = 0; i < 200; i++) {
 5             System.out.println("我在看代码"+i);
 6         }
 7     }
 8     public static void main(String[] args) {
 9         //创建runnbale接口的实现类对象
10         TestRunnable testRunnable = new TestRunnable();
11         //创建线程对象,通过线程对象来开启我们的线程代理
12         Thread thread = new Thread(testRunnable);
13         thread.start();
14         for (int i = 0; i < 1000; i++) {
15             System.out.println("我在学习多线程"+i);
16         }
17     }
18 }

 

 小结

  • 继承Thread类
    • 子类继承Thread类具备多线程能力
    • 启动对象:子类对象.start()
    • 不建议使用:避免OOP单继承局限性
  • 实现Runnable接口
    • 实现接口Runnable具有多线程能力
    • 启动线程:传入目标对象+Thread对象.start()
    • 推荐使用:避免单继承局限性,灵活方便,方便同一对象被多个线程使用

案例:龟兔赛跑-Race

  • 首先来个赛道距离,然后离终点越来越近
  • 判断比赛结束
  • 打印出胜利者
  • 龟兔赛跑开始
  • 兔子要睡觉,乌龟获胜,模拟兔子睡觉
  • 乌龟赢得比赛
 1 public class Race implements Runnable{
 2     //胜利者
 3     private static String winner;
 4 
 5     @Override
 6     public void run() {
 7         for (int i = 1; i <= 100; i++) {
 8             //模拟兔子休息
 9             if (Thread.currentThread().getName().equals("兔子")&&i%10 ==0){
10                 try {
11                     Thread.sleep(1);
12                 } catch (InterruptedException e) {
13                     e.printStackTrace();
14                 }
15             }
16             boolean flag = gameOver(i);
17             if (flag){
18                 break;
19             }
20             System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
21         }
22     }
23     private boolean gameOver(int step){
24         //判断是否有胜利者
25         if (winner!=null){
26             return true;
27         }else {
28             if (step>=100){
29                 winner = Thread.currentThread().getName();
30                 System.out.println("winner is"+winner);
31                 return true;
32             }
33         }
34         return false;
35     }
36 
37     public static void main(String[] args) {
38         Race race = new Race();
39         new Thread(race,"兔子").start();
40         new Thread(race,"乌龟").start();
41     }
42 }

 

 

 

 3.实现Callable接口

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future<Boolean> result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();

3.静态代理

 

Process

posted @ 2020-09-16 22:41  罗晓峥  阅读(402)  评论(0编辑  收藏  举报