博客
关于我
java多线程
阅读量:618 次
发布时间:2019-03-12

本文共 8954 字,大约阅读时间需要 29 分钟。

多线程详解

1、线程简介

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zmzBfqlb-1608209240654)(images_thread/image-20201015213656081.png)]

  • 程序是指令和数据的有序集合,是静态的概念;进程是程序的一次执行过程,是一个动态的概念,是系统资源分配的单位;线程是CPU调度和执行的单位;

    注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核。

  • 在程序运行时,即使自己没有创建线程,后台也会有多个线程,如主线程,gc线程。

  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。

2、线程创建

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Km7i609-1608209240657)(images_thread/image-20201015215048875.png)]

2.1、继承Thread类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nQFZ1o2q-1608209240659)(images_thread/image-20201016141024874.png)]

//下载器class WebDownloader {       public void download(String url, String name) {           try {               FileUtils.copyURLToFile(new URL(url) ,new File(name));        } catch (IOException e) {               e.printStackTrace();        }    }}//实现多线程同步下载图片public class TestThread2 extends Thread{       private String url;    private String name;    public TestThread2(String url, String name) {           this.url = url;        this.name = name;    }    @Override    public void run() {           WebDownloader downloader = new WebDownloader();        downloader.download(url, name);        System.out.println("下载了文件名为:" + name);    }    public static void main(String[] args) {           TestThread2 t1 = new TestThread2("https://pics7.baidu.com/feed/0d338744ebf81a4cbf5a41d981e0105e242da68c.jpeg?token=760f959b12c1adc6be805c125a569b6b", "1.jpg");        TestThread2 t2 = new TestThread2("https://pics3.baidu.com/feed/9358d109b3de9c8231a97ed1ca4fef0d1bd843f2.jpeg?token=766bf62b302cb0d56fb24c4469bb57a2", "2.jpg");        TestThread2 t3 = new TestThread2("https://pics3.baidu.com/feed/314e251f95cad1c8773c1c6ddaf0080ecb3d51cf.jpeg?token=42e2511b4151fcf2d648be6eff246e1d", "3.jpg");        t1.start();        t2.start();        t3.start();    }}

2.2、实现Runnable接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ll7ZWjvR-1608209240662)(images_thread/image-20201016141236171.png)]

public class TestThrea3 implements Runnable{       public void run() {           for (int i = 0; i < 10; i++) {               System.out.println("我在看第-" + i + "-行代码");        }    }    public static void main(String[] args) {           TestThrea3 testThrea3 = new TestThrea3();        new Thread(testThrea3).start();    }}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YmJOm8ZA-1608209240664)(images_thread/image-20201016142101051.png)]

  • Thread其实就是实现了Runnable接口

2.3、小结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlZD6gx4-1608209240666)(images_thread/image-20201016142345073.png)]

public class TestThread4 implements Runnable{       private Integer ticketNum = 10;    public void run() {           while (true) {               if (ticketNum <= 0) {                   break;            }            try {                   Thread.sleep(200);            } catch (InterruptedException e) {                   e.printStackTrace();            }            System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum-- + "张票");        }    }    public static void main(String[] args) {           TestThread4 ticket = new TestThread4();        new Thread(ticket, "小明").start();        new Thread(ticket, "小红").start();        new Thread(ticket, "黄牛党").start();    }}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mHODOJvV-1608209240667)(images_thread/image-20201016143349722.png)]

发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱。

2.4、实现Callable接口(了解即可)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vnq6CKjx-1608209240668)(images_thread/image-20201016191252566.png)]

3、Lamda表达式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jy7wGvP-1608209240669)(images_thread/image-20201016193335931.png)]

3.1、函数式接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SUU9yhTp-1608209240670)(images_thread/image-20201016193605745.png)]

public class TestLambda1 {       public static void main(String[] args) {           //匿名内部类        new Thread(new Runnable() {               public void run() {                   System.out.println("这是一个匿名内部类...");            }        }).start();        //Lambda表达式        new Thread(() -> {               System.out.println("这是一个Lambda表达式...");        }).start();    }}

4、线程方法

4.1、线程停止

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vqpWpKHY-1608209240671)(images_thread/image-20201016201120230.png)]

public class TestStop implements Runnable{       //定义线程中使用的标识    private boolean flag = true    @Override    public void run() {           while (flag) {               System.out.println("run...Thread");        }    }    //对外提供方法改变标识    public void stop() {           this.flag = false;    }}

4.2、线程休眠

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hkd6inY4-1608209240672)(images_thread/image-20201016202551051.png)]

4.3、线程礼让(重新排队)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eyQaiOfZ-1608209240674)(images_thread/image-20201016202731510.png)]

4.4、线程合并(插队)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QC89VuR4-1608209240675)(images_thread/image-20201016203527369.png)]

4.5、wait和sleep的区别

  1. wait是Object类中的方法;而sleep是Thread类中的方法。
  2. 调用wait方法的线程,不会自己唤醒,需要线程调用 notify / notifyAll 方法唤醒等待池中的所有线程,才会进入就绪队列中等待系统分配资源;而sleep方法会自动唤醒,如果时间不到,想要唤醒,可以使用interrupt方法强行打断。
  3. sleep可以在任何地方使用;而wait,notify,notifyAll只能在同步控制方法或者同步控制块中使用。
  4. sleep必须捕获异常;而wait,notify,notifyAll不需要捕获异常。
  5. sleep方法不会释放锁,在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的锁没有被释放,其他线程仍然无法访问这个对象;而wait方法释放了锁,其他线程可以访问该对象。
  6. 线程结束标志:run()方法结束;锁释放的标志:synchronized代码块或方法结束。

5、线程同步

5.1、线程不安全

//线程不安全的集合public class UnSafeList {       public static void main(String[] args) {           List
list = new ArrayList
(); for (int i = 0; i < 1000; i++) { new Thread(() -> { list.add(Thread.currentThread().getName()); }).start(); } System.out.println(list.size()); }}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hpkNkkFJ-1608209240676)(images_thread/image-20201016213707322.png)]

  • 当多个并发同时对非线程安全的集合进行增删改的时候会破坏这些集合的数据完整性。例如上面的ArrayList被多个线程增加数据,不同的线程有可能会相互覆盖同一坐标的值,导致ArrayList的size小于1000。
  • Vector、HashTable、Properties是线程安全的。
  • ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap等都是线程不安全的。

5.2、同步方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c6FrQwOc-1608209240678)(images_thread/image-20201016215043986.png)]

5.3、同步代码块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-etzPmOod-1608209240679)(images_thread/image-20201016220534732.png)]

public class SafeList {       public static void main(String[] args) {           List
list = new ArrayList
(); for (int i = 0; i < 1000; i++) { new Thread(() -> { synchronized (list) { list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(3_000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); }}

5.4、死锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QWCv3zSm-1608209240680)(images_thread/image-20201016222917334.png)]

public void make() throws InterruptedException {           if (choice ==0) {               synchronized (lipstick) {                   //获得口红                System.out.println(this.girlName + "获得了口红");                Thread.sleep(1000);                synchronized (mirror) {                       //还想获得镜子                    System.out.println(this.girlName + "获得了镜子");                }            }        } else {               synchronized (mirror) {                   //获得镜子                System.out.println(this.girlName + "获得了镜子");                Thread.sleep(1000);                synchronized (lipstick) {                       //还想获得口红                    System.out.println(this.girlName + "获得了口红");                }            }        }    }
  • 破除死锁的方法之一:获得资源(也就是锁)之后释放资源

5.5、Lock(锁)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RT9JQXyJ-1608209240681)(images_thread/image-20201017092916429.png)]

private ReentrantLock lock = new ReentrantLock();    public synchronized void run() {           while (true) {               if (ticketNum <= 0) {                   break;            }            lock.lock();            try {                   Thread.sleep(2000);  //sleep不会释放锁对象                System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum-- + "张票");            } catch (InterruptedException e) {                   e.printStackTrace();            } finally {                   lock.unlock();            }        }    }

synchronized与Lock的对比

  • Lock是显式锁(手动的开启和关闭);synchronized是隐式锁,出了作用域就自动释放
  • Lock只有代码块锁,synchronized有代码块锁和方法锁

5.6、线程协作

5.6.1、生产者消费者问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a3tT7PrT-1608209240682)(images_thread/image-20201017094626180.png)]

5.6.2、线程通信

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gX9n4QzO-1608209240684)(images_thread/image-20201017094728489.png)]

5.6.3、解决方法(1)—管程法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e7pRGlfq-1608209240685)(images_thread/image-20201017095134095.png)]

class SynContainer {       Chicken[] chickens = new Chicken[10];   //容器    int count = 0;                          //容器大小    public synchronized void push(Chicken chicken) {     //生产者放入产品        if (count == chickens.length) {     //容器满了---生产者等待,通知消费者            try {                   this.wait();            } catch (InterruptedException e) {                   e.printStackTrace();            }        }        chickens[count] = chicken;   //没有满,生产者生产        count++;        this.notifyAll();    }    public synchronized  Chicken pop() {     //消费者消费        if (count ==0) {                  //容器为0,消费者等待,通知生产者            try {                   this.wait();            } catch (InterruptedException e) {                   e.printStackTrace();            }        }        count--;  //不为空,消费者消费        this.notifyAll();        return chickens[count];    }}

5.6.4、解决方法(2)—信号灯法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8wRh0Jec-1608209240686)(images_thread/image-20201017095252002.png)]

class TV {        //共享资源    private String movie;    private boolean flag = true;    public synchronized void play(String movie) {     //生产者生产        if (!flag) {               try {                   this.wait();            } catch (InterruptedException e) {                   e.printStackTrace();            }        }        System.out.println("演员表演了:" + movie);        this.movie = movie;        this.notifyAll();    //通知观众观看        this.flag = !this.flag;    }    public synchronized void watch() {           if (flag) {     //还没表演            try {                   this.wait();            } catch (InterruptedException e) {                   e.printStackTrace();            }        }        System.out.println("观众观看了" + movie);        this.notifyAll();    //通知观众观看        this.flag = !this.flag;    }}

6、线程池

6.1、优点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NaSt2OxS-1608209240688)(images_thread/image-20201017104503311.png)]

6.2、使用线程池

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E1zxWi8r-1608209240689)(images_thread/image-20201017104649384.png)]

public class TestPool {       public static void main(String[] args) {           ExecutorService service = Executors.newFixedThreadPool(10);        service.execute(new MyTask());        service.execute(new MyTask());        service.shutdown();    }}class MyTask implements Runnable {       @Override    public void run() {           for (int i = 0; i < 5; i++) {               System.out.println(Thread.currentThread().getName() + "正在运行...");        }    }}[] args) {           ExecutorService service = Executors.newFixedThreadPool(10);        service.execute(new MyTask());        service.execute(new MyTask());        service.shutdown();    }}class MyTask implements Runnable {       @Override    public void run() {           for (int i = 0; i < 5; i++) {               System.out.println(Thread.currentThread().getName() + "正在运行...");        }    }}

转载地址:http://tiwaz.baihongyu.com/

你可能感兴趣的文章
MySQL 错误
查看>>
mysql 随机数 rand使用
查看>>
MySQL 面试题汇总
查看>>
MySQL 面试,必须掌握的 8 大核心点
查看>>
MySQL 高可用性之keepalived+mysql双主
查看>>
MySQL 高性能优化规范建议
查看>>
mysql 默认事务隔离级别下锁分析
查看>>
Mysql--逻辑架构
查看>>
MySql-2019-4-21-复习
查看>>
mysql-5.6.17-win32免安装版配置
查看>>
mysql-5.7.18安装
查看>>
MySQL-8.0.16 的安装与配置
查看>>
MySQL-Buffer的应用
查看>>
mysql-cluster 安装篇(1)---简介
查看>>
mysql-connector-java.jar乱码,最新版mysql-connector-java-8.0.15.jar,如何愉快的进行JDBC操作...
查看>>
mysql-connector-java各种版本下载地址
查看>>
mysql-EXPLAIN
查看>>
MySQL-Explain的详解
查看>>
mysql-group_concat
查看>>
MySQL-redo日志
查看>>