多线程

线程生命周期

在这里插入图片描述

1. 线程的生命周期

  1. 新建(NEW):新创建了一个线程对象。

  2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

  3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。

  4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:

(一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
(二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
(三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

  1. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

2. 线程的状态

New

Runnable

Blocked

Waiting

Timed Waiting

Terminated

/**
 * 线程状态测试
**/ 
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                if (i == 5) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        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);
            Thread.sleep(100);
        }
    }
}

3. 创建线程(new)

(1)继承Thread类
  • 继承Thread类,重写run方法
  • 启动线程:子类对象.start()
  • 不建议使用:避免OOP单继承局限性
public class TestThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("----------我在听歌");
        }
    }

    public static void main(String[] args) {
        TestThread t = new TestThread();
        t.start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("我在看电视");
        }
    }
}
(2) 实现Runnable接口
  • 实现Runnable接口,重写run方法
  • 启动线程:传入目标对象+Thread对象.start()(代理模式)
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。
public class TestRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("我在学习---------");
        }
    }

    public static void main(String[] args) {
        TestRunnable runnable = new TestRunnable();
        new Thread(runnable).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("我在听歌");

        }
    }
}

/**
 * 多线程测试: 同一个对象被多个线程使用,抢票问题
 */
public class TestRunnable3 implements Runnable {
    private int ticket = 5;

    public static void main(String[] args) {
        TestRunnable3 runnable3 = new TestRunnable3();
        new Thread(runnable3, "小红").start();
        new Thread(runnable3, "老师").start();
        new Thread(runnable3, "黄牛").start();
    }

    @Override
    public void run() {
        while (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "拿到了第" + ticket-- + "张票");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
练习:模拟多线程——龟兔赛跑
  • 同一条赛道,兔子每隔一段时间休息一次。
public class Race implements Runnable {

    /**
     * 获胜者
     */
    private static String winner;

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            boolean flag = isOver(i);
            if (flag) {
                break;
            }
            //模糊兔子睡觉
            if(Thread.currentThread().getName().equals("兔子") && i % 20 == 0) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
        }
    }

    /**
     * 判断游戏是否结束
     * @return
     */
    public boolean isOver(int steps) {
        if (winner != null && winner != "") {
            return true;
        }
        if (steps == 100) {
            winner = Thread.currentThread().getName();
            System.out.println("获胜者是" + winner);
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race, "乌龟").start();
        new Thread(race, "兔子").start();
    }
}

(3)实现callable接口
  • Callable接口更像是Runnable接口的增强版,相比较Runable接口,Call()方法新增捕获和抛出异常的功能
  • Call()方法可以返回值,调用FutureTask的get()方法来获取返回值
  • Future接口提供了一个实现类FutureTask实现类,FutureTaks类用来保存Call()方法的返回值,并作为Thread类的target。
  • 实现callable接口,重写call方法,需要返回值
/**
 * 线程创建,实现callable接口
 */
public class TestCallable implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"在听歌");
        }
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable callable = new TestCallable();
        FutureTask<Boolean> futureTask = new FutureTask<Boolean>(callable);
        new Thread(futureTask,"张三").start();
        FutureTask<Boolean> futureTask2 = new FutureTask<Boolean>(callable);
        new Thread(futureTask2,"李四").start();
        boolean flag = futureTask.get();
        System.out.println(flag);
    }
}

4. 停止线程(Dead)

标志位——Running->Dead
public class TestStopThread implements Runnable {
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println(i++);
        }
    }

    public synchronized void stop() {
        flag = false;
    }

    public static void main(String[] args) {
        TestStopThread stopThread = new TestStopThread();
        new Thread(stopThread).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main"+i);
            if(i == 90) {
                stopThread.stop();
            }
        }
    }
}

3. 阻塞线程(Block)

sleep——Running->Block
  • sleep 指定阻塞线程的毫秒数,时间到达后线程进入***就绪状态***

  • sleep存在异常InterruptedException

  • 每一个对象都有一个锁,sleep不会释放锁

/**
 * 模拟网络延时,放大线程不安全的问题
**/ 
public class TestSleep implements Runnable {
    private int tickets = 10;

    @Override
    public void run() {
        while (true) {
            if (tickets >= 1) {
                System.out.println(Thread.currentThread().getName() + "拿到了第" + tickets-- + "张票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                break;
            }
        }
    }

    public static void main(String[] args) {
        TestSleep sleep1 = new TestSleep();
        new Thread(sleep1, "小明").start();
        new Thread(sleep1, "老师").start();
        new Thread(sleep1, "黄牛").start();
    }
}
/**
* 模拟倒计时
*/
public class TestSleep2{
  private static int number = 10;

  public static void run() {
      while (true) {
          if (number >= 1) {
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(number--);
          } else {
              break;
          }
      }
  }

  public static void main(String[] args) {
      run();
  }
}

join——Running->Block
  • join相当于插队,其他线程阻塞
package state.join;

/**
 * join测试
 */
public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("vip: " + i + "来了");
        }

    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new TestJoin());
        thread.start();
        for (int i = 0; i < 100; i++) {
            if(i == 88) {
                thread.join();
            }
            System.out.println("当前正在排队:"+i);
        }
    }
}

5. 线程就绪(Runnable)

yield(礼让)——Running->Runnable
  • 让正在进行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让CPU重新调度,礼让不一定成功,看CPU心情

6. 线程优先级 priority

  • 优先级并不一定起作用,只是作为参考,实际调度取决于cpu
public class TestPriority implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "->" + Thread.currentThread().getPriority());
    }

    public static void main(String[] args) {
        TestPriority t = new TestPriority();
        Thread t1 = new Thread(t, "t1");
        Thread t2 = new Thread(t, "t2");
        Thread t3 = new Thread(t, "t3");
        Thread t4 = new Thread(t, "t4");

        t1.start();

        t2.setPriority(3);
        t2.start();

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

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

7. 守护线程 daemon

  • 线程分为守护线程和用户线程

  • 虚拟机必须确保用户线程执行完毕

  • 虚拟机不需要等待守护线程执行完毕

package daemon;

public class TestDaemon {
    public static void main(String[] args) {
        Thread thread = new Thread(new God());
        thread.setDaemon(true);
        thread.start();
        new Thread(new People()).start();
    }
}

class God implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("上帝守护着你");
        }
    }
}

class People implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println("今年" + i + "岁");
            if (i == 100) {
                System.out.println("goodbye");
            }
        }
    }
}  

8. 同步方法和同步块 synchronized

  • 同步块锁的对象是变化的对象,一般为增删改的对象
  • 隐式锁,自动释放
  • 有代码块锁和方法锁
package asyc;

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

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

}
  

9. 死锁

产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之
一不满足,就不会发生死锁。
死锁的解除与预防:
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。

package asyc;

public class DeadLock {
    public static void main(String[] args) {
        Makeup a = new Makeup("白雪公主",0);
        Makeup b = new Makeup("灰姑娘",1);
        new Thread(a).start();
        new Thread(b).start();
    }
}

class Tipstick {
}

class Mirror{

}
class Makeup implements Runnable{
    static private Tipstick tipstick = new Tipstick();
    static private Mirror mirror = new Mirror();
    private int choice;
    private String name;
    Makeup(String name,int choice) {
        this.name = name;
        this.choice = choice;
    }
    public void makeup() throws InterruptedException {
        if( choice == 0) {
            synchronized (tipstick) {
                System.out.println(this.name + "拿到口红");
                Thread.sleep(1000);
//                synchronized (mirror) {   //循环等待,产生死锁
//                    System.out.println(this.name + "拿到镜子");
//                }
            }
            synchronized (mirror) {
                System.out.println(this.name + "拿到镜子");
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.name + "拿到镜子");
                Thread.sleep(3000);
//                synchronized (tipstick) {  // //循环等待,产生死锁
//                    System.out.println(this.name + "拿到口红");
//                }
            }
            synchronized (tipstick) {
                System.out.println(this.name + "拿到口红");
            }
        }
    }

    @Override
    public void run() {
        try {
            this.makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

10. Lock锁

  • 显式定义锁,需要手动关闭锁
  • 代码块锁
  • 性能:Lock锁 > 同步代码块(synchronized) > 同步方法(synchronized)
package asyc;

import java.util.concurrent.locks.ReentrantLock;

public class BuyTickets implements Runnable {
    private int tickets = 1000;
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (tickets > 0) {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "购买了第" + tickets-- + "张票");

            } finally {
                lock.unlock();
            }

        }
    }

    public static void main(String[] args) {
        BuyTickets buyTickets = new BuyTickets();
        new Thread(buyTickets, "小明").start();
        new Thread(buyTickets, "张三").start();
    }
}

生产者消费者问题

  • 单一生产者和单一消费者

  • notifyALl:唤醒所有 wait 线程

  • notify:随机唤醒一个wait线程

管程法
package pc;

public class TestPC {
    public static void main(String[] args) {
        Container container = new Container();
        Producer producer = new Producer(container);
        Consumer consumer = new Consumer(container);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

/**
 * 生产者
 */
class Producer implements Runnable {
    private Container container;

    Producer(Container container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            Chicken chicken = new Chicken(i);
            System.out.println("生产者生产了第" + i + "只鸡");
            container.push(chicken);
        }
    }
}

/**
 * 消费者
 */
class Consumer implements Runnable {
    private Container container;

    Consumer(Container container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("消费了第" + container.pop().getId() + "只鸡");
        }
    }
}

/**
 * 缓冲区
 */
class Container {
    //初始化大小
    Chicken[] chickens = new Chicken[10];
    //计数器,当前存放个数
    int count = 0;

    public synchronized void push(Chicken chicken) {
        if (count == 9) {
            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 product = chickens[count];
        this.notifyAll();
        return product;
    }
}

/**
 * 生产的物品
 */
class Chicken {
    private int id;

    Chicken(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
}

信号灯法
package pc;

public class TestPc2 {
    public static void main(String[] args) {
        Toy toy = new Toy();
        I i = new I(toy);
        You you = new You(toy);
        new Thread(i).start();
        new Thread(you).start();
    }
}

/**
 * 玩具
 */
class Toy {

    boolean flag = true;
    public synchronized void Iplaying() throws InterruptedException {
        if(!flag) {
            this.wait();
        }
        System.out.println("我在玩玩具");
        this.notifyAll();
        flag = !flag;
    }
    public synchronized void Youplaying() throws InterruptedException {
        if(flag) {
            this.wait();
        }
        System.out.println("你在玩玩具");
        this.notifyAll();
        flag = !flag;
    }
}
class I implements Runnable {
    private Toy toy;
    I(Toy toy) {
        this.toy = toy;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i % 2 == 0) {
                try {
                    this.toy.Iplaying();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
class You implements Runnable {
    private Toy toy;
    You(Toy toy) {
        this.toy = toy;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i % 2 != 0) {
                try {
                    this.toy.Youplaying();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

线程池

package pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestPool {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        TestThread testThread = new TestThread();
        service.execute(testThread);
        service.execute(testThread);
        service.execute(testThread);
        service.execute(testThread);
        service.shutdown();
    }
}
class TestThread implements Runnable {

    @Override
    public void run() {
        System.out.println("Hello");
    }
}

扩展

静态代理
  • 类比多线程创建方式

    new Thread(futureTask2,"李四").start();
    

    new Thread 为代理对象,futureTask2为真实对象

  • 下面以结婚为例,对象为真实角色,婚庆公司为真实角色

public class TestStaticProxy {
    public static void main(String[] args) {
        new WeddingCompany(new Person()).marry();
    }
}

/**
 * 结婚
 */
interface Marry {
    void marry();
}

/**
 * 真实角色
 */
class Person implements Marry {

    @Override
    public void marry() {
        System.out.println("小明要结婚了");
    }
}

/**
 * 婚庆公司,代理角色
 */
class WeddingCompany implements Marry {
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    public void before() {
        System.out.println("婚前布置现场");
    }

    public void marry() {
        before();
        target.marry();
        after();
    }

    public void after() {
        System.out.println("婚后收尾款");
    }
}

lambda表达式(函数式接口)
  • 函数式接口:只有一个方法的接口
  • lambda表达式演化过程:外部类->静态内部类->局部内部类->匿名内部类->lambda表达式
  • 类比多线程创建方式 new Thread(futureTask2,"李四").start();Thread类也继承了Runnable接口
/**
 * 推导lambda表达式
 */
public class TestLambda {

    /**
     * 3. 静态内部类
     */
    static class Like implements ILike {
        @Override
        public void like() {
            System.out.println("I like Lambda2");
        }
    }

    public static void main(String[] args) {
        ILike like = new Like();
        like.like();
        /**
         * 4. 局部内部类
         */
        class Like implements ILike {
            @Override
            public void like() {
                System.out.println("I like Lambda3");
            }
        }

        like = new Like();
        like.like();

        /**
         * 4. 匿名内部类,没有类名称
         */
        ILike like2 = new ILike() {
            @Override
            public void like() {
                System.out.println("I like Lambda4");
            }
        };
        like2.like();

        /**
         * 5. lambda表达式
         */
        ILike like3 = () -> System.out.println("I like lambda5");
        like3.like();
    }
}

/**
 * 2. 实现类
 */
class Like implements ILike {
    @Override
    public void like() {
        System.out.println("I like Lambda");
    }
}

/**
 * 1. 函数式接口
 */
interface ILike {
    void like();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值