多线程

概念

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

进程:计算机中的程序关于某数据集合上的一次运行活动,是一个动态的概念,是系统资源分配的单位

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

多线程:真正的多线程是指有多个CPU,即多核

线程创建

继承Thread类(重点)

不建议使用:避免OOP单继承局限性

//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
public class Demo01 extends Thread{

    //run方法,线程体
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在跑线程----"+i);
        }
    }

    //main 线程,主线程
    public static void main(String[] args) {
        
        //创建一个线程对象
        Demo01 demo01 = new Demo01();
        //调用start()方法开启线程
        demo01.start();

        for (int i = 0; i < 700; i++) {
            System.out.println("我是主程序----"+i);
        }
    }
}

注意:线程开启不一定立即执行,由CPU调度

实现Runnable接口(核心)

推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

//创建线程方式二:实现runnable,重写run()方法,执行线程需要丢入runnable接口实现类,调用start方法
public class Demo03 implements Runnable{

    @Override
    public void run() { //run方法,线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在跑线程----"+i);
        }
    }
    
    public static void main(String[] args) {    //main 线程,主线程

        //创建runnable接口的实现类对象
        Demo03 demo03 = new Demo03();
        //创建线程对象,通过线程对象开启线程
        new Thread(demo03).start();
        
        for (int i = 0; i < 700; i++) {
            System.out.println("我是主程序----"+i);
        }
    }
}

实现Callable接口(了解)

callable好处:

  1. 可以定义返回值
  2. 可以抛出异常
//线程创建方式三:实现callable接口
public class Demo05 implements Callable <Boolean>{
    private String url;
    private String name;

    public Demo05(String url,String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载的文件名为:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Demo05 demo021 = new Demo05("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2151136234,3513236673&fm=26&gp=0.jpg","0.jpg");
        Demo05 demo022 = new Demo05("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3791918726,2864900975&fm=26&gp=0.jpg","1.jpg");
        Demo05 demo023 = new Demo05("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3681880960,455182084&fm=26&gp=0.jpg","2.jpg");

        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);

        //提交执行
        Future<Boolean> r1 = ser.submit(demo021);
        Future<Boolean> r2 = ser.submit(demo022);
        Future<Boolean> r3 = ser.submit(demo023);

        //获取结果
        boolean rs1 = r1.get();
        boolean rs2 = r1.get();
        boolean rs3 = r1.get();

        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);

        //关闭服务
        ser.shutdown();
    }
}

多个线程操作同一对象

//多个线程同时操作同一对象
//问题:多个线程操作同一个资源,线程不安全,数据紊乱
public class Demo04 implements Runnable{

    private int ticket = 10;//票数

    @Override
    public void run() {
        while (true){
            if (ticket<=0){
                break;
            }

            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticket--+"张票");

            //模拟延迟
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Demo04 demo04 = new Demo04();

        new Thread(demo04,"小明").start();
        new Thread(demo04,"小红").start();
        new Thread(demo04,"黄牛").start();
    }
}

静态代理

静态代理模式:

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

优点:

  1. 代理对象可以做很多真实对象做不了的事情
  2. 真实对象专注做自己的事情

Lambda表达式

函数式接口(Functional Interface):任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口

Lambda表达式:

  • Lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹
  • 使用Lambda表达式的前提是接口是函数式接口
  • 多个参数也可以去掉参数类型,要去掉就同时去掉,必须加上括号

Lambda表达式推导:

public class Demo06 {

    //3.静态内部类
    static class Like2 implements ILike{
        @Override
        public void like() {
            System.out.println("I Love You!");
        }
    }

    public static void main(String[] args) {

        ILike like = new Like1();//实现类
        like.like();

        like = new Like2();//静态内部类
        like.like();

        //4.局部内部类
        class Like3 implements ILike{
            @Override
            public void like() {
                System.out.println("I Love You!");
            }
        }
        like = new Like3();
        like.like();

        //5.匿名内部类,没有类的名称,必须借助接口或者父类
        like = new ILike() {
            @Override
            public void like() {
                System.out.println("I Love You!");
            }
        };
        like.like();

        //6.Lambda表达式简化
        //like = ()-> System.out.println("I Love You!");
        like = ()->{
            System.out.println("I Love You!");
        };
        like.like();

    }
}

//1.定义一个函数式接口
interface ILike{
    void like();
}

//2.实现类
class Like1 implements ILike{
    @Override
    public void like() {
        System.out.println("I Love You!");
    }
}

线程常用方法

线程停止

  • 不推荐使用 JDK 提供的 stop()、destroy() 方法,已废弃
  • 推荐线程自己停止下来
  • 建议使用一个标志位进行终止变量,当 flag = false,则终止线程运行
//线程停止
/*
1.建议线程正常停止:利用次数,不建议死循环
2.建议使用标志位:设置一个标志位
3.不要使用stop或destroy等过时或JDK不建议使用的方法
*/
public class Demo01 implements Runnable{
    private boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            System.out.println("Thread----运行中");
        }
    }

    public void stopThread(){
        this.flag = false;
    }

    public static void main(String[] args) {
        Demo01 demo01 = new Demo01();
        new Thread(demo01).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main运行中"+i);
            if(i==990){
                demo01.stopThread();
                System.out.println("线程停止了");
            }
        }
    }
}

线程休眠

  • sleep(时间) 指定当前线程阻塞的毫秒数
  • sleep 存在异常
  • sleep 时间达到后线程进入就绪状态
  • sleep 可以模拟网络延迟,倒计时等
  • 每一个对象都有一个锁,slee 不会释放锁
public static void main(String[] args) {
    try {
        down();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //打印当前时间
    Date startTime = new Date(System.currentTimeMillis());
    while (true){
        try {
            Thread.sleep(1000);
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
            startTime = new Date(System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//模拟倒计时
public static void down() throws InterruptedException {
    int num = 10;
    while (true){
        Thread.sleep(1000);
        System.out.println(num--);
        if(num<=0){
            break;
        }
    }
}

线程礼让

礼让不一定成功,看CPU调度

public class Demo03 {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"A").start();
        new Thread(myYield,"B").start();
    }
}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"结束执行");
    }
}

线程插队 Join

Join:合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

public void run() {
    for (int i = 0; i < 100; i++) {
        System.out.println("VIP插队的"+i);
    }
}

public static void main(String[] args) throws InterruptedException {
    Demo04 demo04 = new Demo04();
    Thread thread = new Thread(demo04);
    thread.start();
    for (int i = 0; i < 500; i++) {
        System.out.println("main"+i);
        if (i==250){
            thread.join();
        }
    }
}

线程状态

  • NEW:尚未启动的线程
  • RUNNABLE:在 Java 虚拟机中执行的线程
  • BLOCKED:被阻塞等待监视器锁定的线程
  • WAITING:正在等待另一个线程执行特定动作的线程
  • TIMED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程
  • TERMINATED:已退出的线程
public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(()->{
        for (int i = 0; i < 2; 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){
        Thread.sleep(100);
        state = thread.getState();
        System.out.println(state);
    }
}

注意:线程终端或者结束,一旦进入死亡状态,就不能再次启动

线程优先级

优先级的高低意味着获得调度的概率高低,并不是优先级低就不会被调用,还是要看CPU的调度

建议优先级的设定在 start() 前

public class Demo05 {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);

        t1.start();

        t2.setPriority(Thread.MAX_PRIORITY);
        t2.start();

        t3.setPriority(Thread.MIN_PRIORITY);
        t3.start();

        t4.setPriority(4);
        t4.start();
    }
}

class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
    }
}

守护线程

  • 线程分为用户线程守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
public class Demo06 {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认是false,表示是用户线程,正常的线程都是用户线程

        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 = 0; i < 36500; i++) {
            System.out.println("活着");
        }
        System.out.println("------goodbye world-------");
    }
}

锁机制

线程同步

并发:同一个对象被多个线程同时操作

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

synchronized(隐式锁)

锁机制:当一个线程获得一个对象的排它锁时,独占该资源,其他线程必须等待,线程使用后释放锁

同步方法:synchronized 方法控制对”对象“的访问,每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

public synchronized void method(int args){
    //代码
}

同步块

synchronized(Obj){
    //代码
}

Obj 称为同步监视器

  • Obj 可以是任何对象,但是推荐使用共享资源作为同步监视器。锁的对象是变化的量,需要增删改的对象。
  • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是 class

死锁

死锁:多个线程各自占有一些共享资源,并且互相等待其他线程占有资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况

产生死锁的四个必要条件(只要破解其中一个或多个条件就可以避免死锁发生):

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

Lock锁

Lock锁:提供了对共享资源的独立访问,每次只能有一个线程对 Lock 对象加锁,线程开示访问共享资源之前应获得 Lock 对象

ReentrantLock(可重入锁):实现了 Lock,它拥有与 synchronized 相同的并发性和内存语义,可以显式加锁、释放锁

public class Demo07 {
    public static void main(String[] args) {
        TestLock testLock = new TestLock();

        new Thread(testLock).start();
        new Thread(testLock).start();
        new Thread(testLock).start();
    }
}

class TestLock implements Runnable{

    int ticketNum = 10;

    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
    while (true){
        try {
            lock.lock();//加锁
            if (ticketNum>0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"--->"+ticketNum--);
        }else {
            break;
        }
        }finally {
            //解锁
            //如果同步代码有异常,要将unlock()写入finally语句块
            lock.unlock();
        }
    } 
    }
}

synchronized 与 Lock 对比

  • Lock 是显示锁(手动开启和关闭锁,别忘记关闭锁),synchronized 是隐式锁,出了作用域自动释放

  • Lock 只有代码块锁,synchronized 有代码块锁和方法锁

  • 使用 Lock 锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)

  • 优先使用顺序:

    Lock > 同步代码块 > 同步方法

线程协作

生产者消费者模型:线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件

线程通信方法:

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

注意:只能在同步方法或者同步代码块中使用

管程法

//测试:生产者消费者模型--->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class Demo08 {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();

        new Productor(container).start();
        new Consumer(container).start();
    }
}

//生产者
class Productor extends Thread{
    SynContainer container;
    public Productor( SynContainer container){
        this.container = container;
    }
    //生产
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了"+i+"只鸡");
        }
    }
}

//消费者
class Consumer extends Thread{
    SynContainer container;
    public Consumer( SynContainer container){
        this.container = container;
    }
    //消费
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了--->"+container.pop().id+"只鸡");
        }
    }
}

//产品
class Chicken{
    int id;//产品编号
    public Chicken(int id) {
        this.id = id;
    }
}

//缓冲区
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){
            //等待生产者生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];
        //通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

信号灯法

//测试:生产者消费者问题--->信号灯法,标志位解决
public class Demo09 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生产者--->演员
class Player extends Thread{
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("新闻联播");
            }else {
                this.tv.play("脑白金");
            }
        }
    }
}

//消费者--->观众
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品--->节目
class TV{
    //演员表演,观众等待 T
    //观众观看,演员等待 F
    String readio;//表演的节目
    boolean flag = true;

    //表演
    public synchronized void play(String readio){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了:"+readio);
        //通知观众观看
        this.notifyAll();
        this.readio = readio;
        this.flag = !flag;
    }
    
    //观看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了:"+readio);
        //通知演员表演
        this.notifyAll();
        this.flag = !flag;
    }
}

线程池

线程池相关API:ExecutorService 和 Executors

ExecutorService:真正的线程池接口

  • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
  • Futuresubmit(Callabletask):执行任务,有返回值,一般用来执行Callable
  • void shutdown():关闭连接池

Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

public class Demo01 {
    public static void main(String[] args) {
        //1.创建服务,创建线程池
        //newFixedThreadPool 参数为线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);

        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //2.关闭连接
        service.shutdown();
    }
}

class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
posted @ 2021-03-28 11:05  %新手上路%  阅读(55)  评论(0)    收藏  举报