Java 多线程

多线程

多线程的实现(重点)

继承Thread类

  1. 自定义线程类继承Thread类

  2. 重写run()方法,编写线程执行体

    public class TestThread extendx Thread{
        @Override
        public void run() {}
    }
    
  3. 创建线程对象,调用start()方法启动线程

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

实现Runnable接口

  1. 定义MyRunnable类实现Runnable接口

  2. 实现run()方法,编写线程执行体

    public class MyRunnable implements Runnable{
         @Override
        public void run() {}
    }
    
  3. 创建线程对象,调用start()方法启动线程

    public static void main(String[] args) {
           //创建runnable接口的实现类对象
            TestThread3 testThread3 = new TestThread3();
        
            //创建线程对象,通过线程对象来启动我们的线程,代理
    //        Thread thread = new Thread(testThread3);
    //        thread.start();
            //简化
            new Thread(testThread3).start();
    }
    

实现Callable接口(了解即可)

  1. 实现Callable接口,需要返回值类型

  2. 重写call方法,需要抛出异常

    public class TextCallable2 implements Callable<Boolean> {
         @Override
        public Boolean call() throws Exception {}
            return true;
        }
    
  3. 创建目标对象

  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);

  5. 提交执行:Future result1 = ser.submit(t1);

  6. 获取结果:boolean r1 = result1.get();

  7. 关闭服务:ser.shutdownNow();

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建目标对象
        TextCallable2 t1 = new TextCallable2();
        TextCallable2 t2 = new TextCallable2();
        
            //创建执行服务
            ExecutorService ser = Executors.newFixedThreadPool(2);
        
            //提交执行
            Future<Boolean> f1 = ser.submit(t1);
            Future<Boolean> f2 = ser.submit(t2);
    
            //获取结果
            boolean b1 = f1.get();
            boolean b2 = f2.get();
    
            //关闭服务
            ser.shutdownNow();
    
        }
    

总结

  • 继承Thread类

    • 子类继承Thread类具备多线程能力
    • 启动线程:子类对象 . start()
    • 不建议使用: 避免OPP单继承局限性

  • 实现Runnable接口

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

  • 实现Callable接口

    • 了解即可

Lambda表达式的推导

推导过程

1. 实现类
public class TestLambda {
    
    //主方法
     public static void main(String[] args) {
         ILike like = new Like();
        like.Lambda();
     }
}

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

// 实现类
class Like implements ILike{

    @Override
    public void Lambda() {
        System.out.println("i like lambda");
    }
}
  1. 静态内部类
public class TestLambda {
    
    // 静态内部类
    static class Like implements ILike{

        @Override
        public void Lambda() {
            System.out.println("i like lambda");
        }
    }
    
    // 主方法
     public static void main(String[] args) {
         ILike like = new Like();
        like.Lambda();
     }
}

// 定义一个函数式接口
interface ILike{
    void Lambda();
}
  1. 局部内部类
public class TestLambda {
    
    //主方法
     public static void main(String[] args) {
         
         // 局部内部类
        class Like implements ILike{

            @Override
            public void Lambda() {
                System.out.println("i like lambda");
            }
        }
         
         ILike like = new Like();
        like.Lambda();
     }
}

// 定义一个函数式接口
interface ILike{
    void Lambda();
}
  1. 匿名内部类
public class TestLambda {
    
    //主方法
     public static void main(String[] args) {
         
        // 匿名内部类: 没有类的名称,必须借助接口或者父类
        ILike like = new ILike() {
            
            @Override
            public void Lambda() {
                System.out.println("i like lambda");
            }
        };
         
        like.Lambda();
         
     }
}

// 定义一个函数式接口
interface ILike{
    void Lambda();
}
  1. lambda表达式
public class TestLambda {
    
    //主方法
     public static void main(String[] args) {
         
         // lambda表达式
        ILike like =()-> {
            System.out.println("i like lambda");
        };
        like.Lambda();
     }
}

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

5.1 lambda表达式有参数

public class TestLambda {
    
    //主方法
     public static void main(String[] args) {
         
         // lambda表达式
        ILike like =(int a)-> {
            System.out.println("i like lambda --> " + a);
        };
        like.Lambda(520);
     }
}

// 定义一个函数式接口
interface ILike{
    void Lambda(int a);
}

5.2. lambda简化 :

  1. 去掉括号

    只有一个参数时能简化去掉括号 和 参数类型

    ILike like =a -> {
        System.out.println("i like lambda"+a);
    };
            like.Lambda(520);
    

    如果有多个参数,不能去掉括号

    ILike like = (a,b,c) -> {
        System.out.println("i like lambda"+a);
    };
            like.Lambda(520,true,"man");
    
  2. 去掉大括号

​ 只有一行代码的情况下才能简化成为一行.

 ILike like =(a,b,c)-> System.out.println("i like lambda" + a);
        like.Lambda(520,true,"man");

​ 如果有多行,那么就用代码块包裹。

 ILike like =(a,b,c)-> {
     System.out.println("i like lambda --> " + a);
     System.out.println("i like lambda too --> " + b);
 };
        like.Lambda(520,true,"man");

总结

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

线程状态

停止线程

  • 不推荐使用JDK提供的stop()、destroy()方法。【已废弃】

  • 推荐线程自己停止下来

  • 推荐使用一个标志位进行终止变量,当flag = false,则终止线程运行

    public class TestStop implements Runnable{
    
        // 1.设置一个标志位
        private boolean flag = true;
    
        @Override
        public void run() {
            // 2.线程体使用该标识
            while (flag){
                System.out.println("run ... Thread");
            }
        }
    
        // 3. 设置一个公开的方法停止线程,转换标志位
        public void tingzhi(){
            this.flag = false;
        }
    

线程休眠

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

网络延时
// 模拟网络延时 : 放大问题的发生性。
public class TestSleep implements Runnable{
    //票数
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true) {
            if (ticketNums <= 0){
                break;
            }
            //模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("模拟延迟有问题");
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第" + ticketNums-- + "票");
        }
    }

    public static void main(String[] args) {

        TestSleep testsleep = new TestSleep();

        new Thread(testsleep,"学生").start();
        new Thread(testsleep,"老师").start();
        new Thread(testsleep,"黄牛").start();
    }
}

倒计时
//模拟倒计时。。。
public class TestSleep2 {

    public static void main(String[] args) {
        try {
            tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void tenDown() throws InterruptedException {
        int num = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if(num <= 0){
                break;
            }
        }
    }

}

打印当前系统时间
 public static void main(String[] args) {
        //打印当前系统时间
        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();
            }
        }
    }

线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞

  • 将线程从运行状态转为就绪状态

  • 让CPU重新调度,礼让不一定成功! 看CPU心情

    // 礼让线程
    // 礼让不一定成功,看CPU心情
    public class TestYield {
    
        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 class TestJoin implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(100);// 让VIP晚来一会儿
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 200; i++) {
                System.out.println(Thread.currentThread().getName()+"我要先执行"+i+"次");
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            //启动我们的线程
            TestJoin testJoin = new TestJoin();
            Thread thread = new Thread(testJoin, "VIP:");
            thread.start();
            //主线程
            for (int i = 0; i < 100; i++) {
                if (i == 50){
                    thread.join();//插队
                }
                System.out.println("正在执行"+i+"次");
            }
        }
    }
    

线程状态的观测

  • NEW: 尚未启动的线程处于此状态。
  • RUNNABLE: 在Java虚拟机中执行的线程处于此状态
  • 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 <= 5; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("");
        });

        //观察状态
        Thread.State state = thread.getState();
        System.out.println(state); //NEW

        //观察启动后
        thread.start();//启动线程
        state = thread.getState();//观察
        System.out.println(state);//RUNNABLE

        while (state != Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
            Thread.sleep(100);
            state = thread.getState();//更新线程状态
            System.out.println(state);//输出状态
        }
    }
}

线程优先级

  • 线程的优先级用数字来表示,范围从1~10.
    • Thread.MIN_PRIORITY = 1;
    • Thread.MAX_PRIORITY = 10;
    • Thread.NORM_PRIORITY = 5;
    • 优先级小于0 或者 大于10,会报错
  • 使用一下方式改变或获取优先级
    • getPriority() . setPriority(int xxx)
    • 优先级的设定建议在start()调度前
  • 优先级低只是意味着获取调度的概率低,并不是优先级低就不会被调度。这都是看CPU的调度
//测试线程的优先级
public class TestPriority {
    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);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);

        //先设置优先级,在启动
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10
        t4.start();

        t5.setPriority(8);
        t5.start();

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

class MyPriority implements Runnable{

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

守护线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如,后台记录操作日志,监控内存,垃圾回收等待…
//测试守护线程
public class TestDaemon {
    public static void main(String[] args) {
        Bodyguard bodyguard = new Bodyguard();
        Me me = new Me();

        Thread thread = new Thread(bodyguard);
        //开启守护线程
        thread.setDaemon(true);// 默认是false表示是用户线程,正常的线程都是用户线程。。。
        thread.start();// 守护线程启动

        new Thread(me).start();// 我  用户线程启动...

    }
}

// 卫士
class Bodyguard implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("我将守护你");
        }
    }
}

//我
class Me implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("我已存活"+i+"年");
        }
        System.out.println("==== goodbye world ====");
    }
}

线程同步(重点)

synchronized 方法 和 synchronized块

  • synchronized 方法

同步方法: public synchronized void method (int arg){}

缺点:消耗资源

/不安全买票
//线程不安全,有负数,有买到同一张票
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuiTicket buiTicket = new BuiTicket();
        new Thread(buiTicket,"张三").start();
        new Thread(buiTicket,"李四").start();
        new Thread(buiTicket,"王五").start();
    }
}

class BuiTicket implements Runnable{

    //票
    private int ticketNums = 10;
    boolean flog = true;
    @Override
    public void run() {
        //买票
        while (flog){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

   //synchronized 同步方法,锁的是this
    private synchronized void buy() throws InterruptedException {
        if(ticketNums <= 0){
            flog = false;
            return;
        }
        //模拟演示
            Thread.sleep(1000);
        //买票

        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }
}
  • synchronized块

同步块: synchronized(Obj){ }

//不安全取钱
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(1000,"存款");
        Drawing you = new Drawing(account,50,"你");
        Drawing hem = new Drawing(account,100,"她");

        you.start();
        hem.start();
    }
}

//账户
class Account{
    int money; //余额
    String name;//卡名

    public Account(int money, String name){
        this.money = money;
        this.name = name;
    }
}

//银行:模拟取款
class Drawing extends Thread{
    Account account;//账户
    //取了多少钱
    int drawingMoney;
    //现在
    int nowMoney;

    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;

    }

    //取钱


    @Override
    public  void run() {

        //synchronized块
        //锁的对象就是变化的量,需要增删改的对象
        synchronized (account){
            //判断有没有钱
            if (account.money - drawingMoney < 0){
                System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
                return;
            }

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

            //卡内余额 = 余额 - 你取的钱
            account.money = account.money - drawingMoney;
            //你手里的钱
            nowMoney = nowMoney + drawingMoney;

            System.out.println(account.name + "余额" + account.money);
            System.out.println(this.getName()+"手里的钱:" + nowMoney);
        }
    }
}

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

JUC安全类型的集合

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

死锁

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

    • 互斥:在一段时间内某资源仅为一进程所占用
    • 请求与保持:当进程因请求资源而阻塞时,对已获得的资源保持不放
    • 不可剥夺:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放
    • 循环等待:在发生死锁时,必然存在一个进程—资源的环形链
  • 预防和破坏死锁

    • 资源一次性分配:如果某进程所需的资源能够一次分配完则分配,否则不分配
    • 申请新资源,必须先释放所拥有的的其他资源,若还需要此资源,需向系统重新申请
    • 申请新资源失败,释放所拥有的的所有资源
    • 对资源进行编号,申请编号小的资源必须先释放所拥有的编号大的资源
    • 当产生死锁时,强制结束一些进程

public class DeadLock {
    public static void main(String[] args) {
        MakeUp g1 = new MakeUp(0,"张三");
        MakeUp g2 = new MakeUp(1,"李四");

        g1.start();
        g2.start();
    }
}

// 口红
class Lipstick{

}

// 镜子
class Mirror{

}

class MakeUp extends Thread{

    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice; // 选择
    String girlName; // 使用化妆品的人

    public MakeUp(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

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

    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() throws InterruptedException {
        if (choice == 0){
            synchronized (lipstick){ //获得口红的锁
                System.out.println(this.girlName+"获得口红的锁");
                Thread.sleep(3000);
                synchronized (mirror){// 一秒钟后想获得镜子
                    System.out.println(this.girlName + "获得镜子的锁");
                }
            }
        }else {
            synchronized (mirror){//获得镜子的锁
                System.out.println(this.girlName+"获得镜子的锁");
                Thread.sleep(6000);
                synchronized (mirror){// 一秒钟后想获得口红
                    System.out.println(this.girlName + "获得口红的锁");
                }
            }
        }
    }
}

解决死锁:

//化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() 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(2000);
            }
            synchronized (mirror){// 一秒钟后想获得口红
                System.out.println(this.girlName + "获得口红的锁");
            }
        }
    }

Lock(锁)

  • 比较常用的是ReenTrantLock,可以显示加锁、释放锁。
class A{
    //定义look值
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
		//加锁
		lock.lock();
		try {
			// 保证线程安全的代码;      
		}
        finally {
		//解锁
		lock.unlock();
		//如果同步代码有异常,要将unlock()写入finally语句块
		}
	}
}

  • synchronized 和 Lock 的对比
    • Lock是显式锁(手动开启和关闭锁),synchronized是隐式锁(出了作用域自动释放)
    • Lock只有代码块锁,synchronized有代码块锁和方法锁
    • Lock的性能更好,而且提供更多的子类(扩展性)
    • 优先使用顺序:
      • Lock > 同步代码块 > 同步方法

线程通信问题

生产者—消费者问题

方法名作用
wait( )表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁。
wait( long tomeout )指定等待的毫秒数
notify( )唤醒一个处于等待状态的线程
notifyAll( )唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
解决方法1:利用缓冲区解决(管程法)
  • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
  • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)
  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个 “缓冲区”

生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

//测试: 生产者消费者问题 --> 利用缓冲区解决:管程法

// 生产者,消费者,产品,缓冲区
public class TestPC {
    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;
    }

 }


解决方法2:信号灯法,标志位解决
//测试生产者和消费者问题2:信号灯法,标志位解决
public class TestPC2 {
    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 voice; // 表演的节目
    boolean flag = true;

    //表演
    public synchronized void play(String voice){

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

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

线程池

  • 思路:

    • 提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。
  • 好处:

    • 提高响应速度
    • 降低资源消耗
    • 便于资源管理(…)
  • ExecutorService 和 Executors

  • ExecutorService : 真正的线程池接口。常见子类ThreadPoolExecutor

    • void execute(Runnable command) : 执行任务/命令,没有返回值,一般用来执行Runnable
    • Future submit(Callable task) : 执行任务,有返回值,一般用来执行Callable
    • void shutdown() : 关闭连接池
  • Executors :工具类、线程池的工厂类,用于创建并返回不同类型的线程池

// 线程池
public class TestPool {
    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());
    }
}

总结

//回顾总结线程的创建
public class ThreadNew {
    public static void main(String[] args) {
        new MyThread1().start();

        new Thread(new MyThread2()).start();

        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread3());
        new Thread(futureTask).start();

        try {
            Integer integer = futureTask.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

// 1.继承Thread类
class MyThread1 extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread1");
    }
}

// 2.实现Runnable 接口
class MyThread2 implements Runnable{

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

// 3.实现Callable接口
class MyThread3 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("MyThread3");
        return 100;
    }
}

以上内容来源于:狂神说

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值