多线程

Posted python-road

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。

多线程

进程是执行程序的一次执行过程,是系统资源分配的单位;一个进程可以包含若干个线程,线程是CPU调度和执行的单位;

一.继承Thread类

  • 自定义线程类继承Thread类
  • 重写run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
package oop;
//线程开启不一定执行,由CPU调度执行
public class Test2 extends Thread{
    @Override
    public void run() {
        for(int i=0;i<10;i++)
        System.out.println("看什么呢");
    }

    public static void main(String[] args) {
        Test2 test2=new Test2();
        test2.start();
        for (int i=0;i<1000;i++){
            System.out.println("我在吃"+i);
        }
    }
}

网图下载

package oop;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

//实现多线程同步下载图片
public class Test2 extends Thread{
    private String url;
    private String name;

    public Test2(String url,String name){
        this.name=name;
        this.url=url;
    }
    @Override
    public void run() {
        WebDownloader webDownloader=new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载的文件名围殴"+name);
    }

    class WebDownloader{
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    }

    public static void main(String[] args) {
        Test2 test1=new Test2("https://t8.baidu.com/1.jpg","1.jpg");
        Test2 test2=new Test2("https://t8.baidu.com/2.jpg","2.jpg");
        Test2 test3=new Test2("https://t8.baidu.com/3.jpg","3.jpg");
        test1.start();
        test2.start();
        test3.start();
    }
}


二.实现Runable接口

创建线程方法2:实现runable接口,重写run方法,执行线程需要丢入runable接口实现类,调用start方法

package oop;
public class Test2 implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<10;i++)
            System.out.println("看什么呢");
    }

    public static void main(String[] args) {
       //创建runnable接口的实现类对象
        Test2 test2=new Test2();
        Thread thread=new Thread(test2);
        thread.start();
        for(int i=0;i<10;i++){
            System.out.println("我在学习!");
        }
        }
}

三.继承Thread类和实现Runnable接口对比

  • 继承Thread类
    • 子类继承Thread类具备多线程能力
    • 启动线程:子类对象.start()
    • 不建议使用:单继承局限性
  • 实现Runnable接口
    • 实现接口Runnable具有多线程能力
    • 启动线程:传入目标对象+Thread对象.start()
    • 可以避免单继承局限性,灵活,方便同一对象被多个线程使用
      多线程同时使用实现Runnable接口对象
package oop;
public class Test2 implements Runnable {
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true) {
            if (ticketNums <= 0) {
                break;
            }
            System.out.println(Thread.currentThread().getName() + "拿到了底" + ticketNums + "票");
             ticketNums--;
        }
    }

    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        Test2 test2 = new Test2();
        new Thread(test2, "ab").start();
        new Thread(test2, "cd").start();
        new Thread(test2, "ef").start();
        new Thread(test2, "hi").start();

    }
}

四.静态代理

真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色

package oop;
public class Test2{
    public static void main(String[] args) {
        WeddingCompany weddingCompany=new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }

}
interface Marry{
    void HappyMarry();
}
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("结婚了");
    }
}
class WeddingCompany implements Marry{
    private You target;
    public  WeddingCompany(You target){
        this.target=target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }
    private void before(){
        System.out.println("结婚前");
    }
    private void after(){
        System.out.println("结婚后");
    }
}

代理的好处:代理对象可以做很多真实对象做不了的事情
真实对象能专注做直接的事情

五.lambda表达式

函数式接口:任何接口如果只包含一个抽象方法,那么他就是一个函数式接口;对于函数式接口,可以通过lambda表达式来创建该接口的对象

package oop;
public class Test2{
    public static void main(String[] args) {
        //形式1.
        Like like1= (int a)->{ System.out.println("我爱你"); };
        //形式2.
        Like like2=(a)->{
            System.out.println("你忙吧");
        };
        Like like3=a-> System.out.println("hhh");
       like1.lambda(1);
       like2.lambda(1);
       like3.lambda(1);
    }
}
interface Like{
    void lambda(int a);
}

六.线程停止

通过外部标志位和外部停止方法进行停止

package oop;
public class Test2 implements Runnable{
    private boolean flag=true;
    @Override
    public void run() {
        int i=0;
        while(flag){
            System.out.println("Run"+i++);
        }
    }
    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        Test2 test2=new Test2();
        new Thread(test2).start();
        for(int i=0;i<100;i++){
            System.out.println("main"+i);
            if(i==90){
                System.out.println("线程该停止了");
                test2.stop();
            }
        }
    }
}

七.线程休眠

  • sleep(时间)指定当前线程阻塞的毫秒数;1000毫秒=1秒
  • sleep存在异常InterruptedException
  • sleep时间达到后线程进入就绪状态
  • 可以模拟倒计时和网络延时
  • 每个对象都有锁,sleep不会释放锁

八.线程礼让

  • 礼让线程,让当前正在执行的线程暂停,单不阻塞
  • 将线程从运行状态转为就绪状态
  • 让cpu重新调度,礼让不一定成功,看CPU调度
package oop;
public class Test2{
    public static void main(String[] args) {
        MyYield myYield=new MyYield();
        new Thread(myYield,"dwx").start();
        new Thread(myYield,"zzz").start();
    }
}
class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

线程状态

  • NEW:尚未启动的线程处于此状态
  • RUNNABLE:在Java虚拟机中执行的线程处于此状态
  • BLOCKED:被阻塞等待监视器锁定的线程处于此状态
  • WAITING:正在等待另一个线程执行特定动作的线程处于此状态
  • TIMED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
  • TERMINATED:已退出的线程处于此状态
package oop;
public class Test2{
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            for(int i=0;i<5;i++){
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
            System.out.println("------");
        });
        Thread.State state=thread.getState();
        System.out.println(state);
        thread.start();
        state=thread.getState();
        System.out.println(state);
        while(state!=Thread.State.TERMINATED){
            state=thread.getState();
            System.out.println(state);
        }
        state=thread.getState();
        System.out.println(state);
    }
}

十.线程的优先级

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定调度哪个线程来执行
  • 线程的优先级用数字表示,范围从1-10
    - Thread.MIN_PRIORITY=1;
    - Thread.MAX_PRIORITY=10;
    - Thread.NORM_PRIORITY=5;
    改变或获取优先级
    getPriority(); setPriority(int XXX)
    设置在start()之前

十一.守护线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 守护线程有后台记录操作日志,监控内存,垃圾回收等
package oop;
public class Test2{
    public static void main(String[] args) {
        God god =new God();
        You you =new You();
        Thread thread=new Thread(god);
        thread.setDaemon(true);//默认为false,普通线程,设置为true后为守护线程。
        thread.start();
        new Thread(you).start();


    }
}
class God implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("上帝保佑你");
        }
    }
}
class You implements  Runnable{
    @Override
    public void run() {
        for(int i=1;i<365;i++){
            System.out.println("这是我生命中的第"+i+"天");
        }
    }
}


十二.线程同步

并发:同一个对象被多个线程同时操作
线程同步:其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

同步方法和同步块

给方法加上synchronized关键字即可,synchronized方法控制对对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,同步块就是synchronized(obj){},要同步就是要锁变化的量

package oop;

import java.util.ArrayList;
import java.util.List;

//不安全的买票
public class UnsafeList{
    public static void main(String[] args) {
        List<String> list=new ArrayList<>();
        for(int i=0;i<1000;i++){
            new Thread(()->{
                synchronized(list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(100);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

死锁

产生死锁的四个必要条件:

  • 互斥条件:一个资源每次只能被一个进程使用
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:进程已获得的资源在为使用完之前,不能强行剥夺
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

只要破解其中的任意一个或多个条件就能避免死锁

Lock(锁)

java.util.concurrent.locks.Lock接口,ReentrantLock类实现了Lock,可以显式加锁释放锁

 ReentrantLock lock=new ReentrantLock();
        lock.lock();//加锁
        lock.unlock();//解锁

十三.线程通信

java提供了几个方法解决线程之间的通信问题

  • wait():表示线程一直等待,知道其他线程通知,与sleep不同,会释放锁
  • wait(long timeout):指定等待的毫秒数
  • notify():唤醒一个处于等待状态的线程
  • notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

解决生产者消费者问题

两种解决方法:

  1. 管程法:生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
  2. 信号灯法:增加标志位,用标志位来通知生产者和消费者谁唤醒谁等待

十四.线程池

提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁,实现重复利用

优点:

  • 提高响应速度
  • 降低资源消耗
  • 便于线程管理
package oop;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool{
    public static void main(String[] args) {
        //创建线程池
        ExecutorService service= Executors.newFixedThreadPool(10);
        //执行
        service.execute(new MyThread());
        //关闭连接
        service.shutdown();
    }

}
class  MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}










以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章

线程学习知识点总结

多个请求是多线程吗

python小白学习记录 多线程爬取ts片段

多线程编程

多线程编程

python多线程