JAVA多线程的使用方法(从代码理解定义)

1、多线程的创建

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

1.1创建方式一

继承Thread类,重写run()方法,调用start()方法开启线程。

package SunThread;

public class Thread_01 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在写代码!");
        }
    }

    public static void main(String[] args) {
        //main线程,主线程
        //创建线程对象
        Thread_01 thread_01 = new Thread_01();
        //调用start开启线程
        thread_01.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学JAVA");
        }
    }
}

image-20201031134111826

多次运行发现(最好修改循环输出次数多一点),输出结果一般会不同,原因就是CPU在进行线程的调度。

1.2创建方式二(建议使用)

实现Runnable接口,重写run()方法,实现接口需要丢入Runnable接口实现类,调用start()方法开启线程。

package SunThread;

public class Thread_03 implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
             System.out.println("我在写代码");
        }
    }

    public static void main(String[] args) {
        //main线程,主线程
        //创建ruunnable实现类对象
        Thread_03 thread_03 = new Thread_03();
        //创建线程对象通过线程对象实现我们的接口类
		//Thread thread = new Thread(thread_03);
		//thread.start();
        new Thread(thread_03).start();//与上两行实现的结果是一样的
        for (int i = 0; i < 100; i++) {
           System.out.println("我在学JAVA");
        }
    }
}

image-20201031134702704

推荐使用:实现Runable()接口,因为可以有效的避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用

1.3创建方式三(不经常使用)

​ 与使用runnable方式相比,callable功能更强大些:call方法可以有返回值,方法可以抛出异常,支持泛型的返回值,需要借助FutureTask类,获取返回结果等。

运用三个线程下载网络图片的方式,对Callable实现接口类的方式进行解释,(代码直接复制运行不了)。

package SunThread;

import org.apache.commons.io.FileUtils;


import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

public class Thread_05 implements Callable<Boolean> {
    private String url;//下载的网络图片地址
    private String name;//保存的文件名

    public Thread_05(String url, String name) {
        this.url = url;
        this.name = name;
    }
    @Override
    public Boolean call() throws Exception {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.Downloader(url,name);
        System.out.println("文件name为"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //new 3个Callable接口的对象
        Thread_05 t1 = new Thread_05("https://img-blog.csdnimg.cn/20201029222157503.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NVTl9fQ0dK,size_16,color_FFFFFF,t_70#pic_center","1.jpg");
        Thread_05 t2 = new Thread_05("https://img-blog.csdnimg.cn/20201029222523262.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NVTl9fQ0dK,size_16,color_FFFFFF,t_70#pic_center","2.jpg");
        Thread_05 t3 = new Thread_05("https://img-blog.csdnimg.cn/20201029222403840.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NVTl9fQ0dK,size_16,color_FFFFFF,t_70#pic_center","3.jpg");
        //创建固定线程个数为3个的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        //提交执行线程,最多3个
        Future<Boolean> s1 = executorService.submit(t1);
        Future<Boolean> s2 = executorService.submit(t2);
        Future<Boolean> s3 = executorService.submit(t3);
        //获取相应的结果
        boolean rs1=s1.get();
        boolean rs2=s2.get();
        boolean rs3=s3.get();
        //关闭线程池
        executorService.shutdown();
    }
}
//具体的下载方法,使用时需要导入commons—IO-2.6.jar包
class WebDownloader{
    public void Downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

image-20201031151212646

注:中间并发进行的接口对象,根据自己需要进行修改。

2、线程五大状态

2.1线程的停止(stop)

1.建议线程正常停止―>利用次数,不建议死新环.

2.建议使用标志位--->设置一个标志位

3.不要使用stop或者destroy等过时或者JDK不建议使用的方法

package SunThread;
//测试线程停止
public class Threadstop_01 implements Runnable{
    private boolean flag=true;
    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("我在测试线程停止"+i++);
        }
    }
    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        Threadstop_01 threadstop_01 = new Threadstop_01();

        new Thread(threadstop_01).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main"+i);
            if(i==900)
            {
                threadstop_01.stop();
                System.out.println("线程停止了");
            }
        }

    }
}

image-20201031165812618

2.2线程的休眠(sleep)

1、sleep(时间)指定当前线程阻塞的毫秒数;

2、sleep存在异常InterruptedException;

3、sleep时间达到后线程进入就绪状态;

4、sleep可以模拟网络延时,倒计时等。

5、每一个对象都有一个锁,sleep不会释放锁;

package SunThread;
//runnable实现买票过程
//sleep放大问题的发生性
public class Thread_04 implements Runnable{
    private int a=10;
    @Override
    public void run() {
        while(a>0){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+a--+"张票");
        }
    }

    public static void main(String[] args) {
        Thread_04 thread_04 = new Thread_04();
        new Thread(thread_04,"小明").start();
        new Thread(thread_04,"小李").start();
        new Thread(thread_04,"小红").start();

    }
}

image-20201031171313334

注:sleep放大问题的发生性,运用sleep可以编写倒计时、模拟网络延时等等。

2.3线程的礼让(yield)

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

2、将线程从运行状态转为就绪状态;

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

package SunThread;

import javax.xml.transform.sax.SAXSource;

public class ThreadYield_01 implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"该线程开始执行!");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"该线程停止执行!");
    }
    public static void main(String[] args) {
        ThreadYield_01 threadYield_01 = new ThreadYield_01();
        new Thread(threadYield_01,"A").start();
        new Thread(threadYield_01,"B").start();
    }
}

image-20201031172234766

礼让不一定会成功

2.4线程的强制执行(join)

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

2、可以想象成插队

package SunThread;

public class ThreadJoin_01 implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 1000; i++) {
            System.out.println("线程vip来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadJoin_01 threadJoin_01 = new ThreadJoin_01();
        Thread thread = new Thread(threadJoin_01);

        thread.start();

        for (int i = 0; i < 1000; i++) {
            if(i==500)
            {
                thread.join();
            }
            System.out.println("main"+i);
        }
    }
}

image-20201031174008986

由于现在CPU一般多核进行处理,我们可以在run()方法中设置一个休眠时间。

3、线程的状态

3.1运用代码的输出线程状态

1、NEW 尚未启动的线程处于此状态。

2、RUNAABLE 在Java虚拟机中执行的线程处于此状态。

3、BLOCKED 被阻塞等待监视器锁定的线程处于此状态。
4、WAITING 正在等待另一个线程执行特定动作的线程处于此状态。

5、TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。

6、TEPM4INATED 已退出的线程处于此状态。

package SunThread;

public class Thread_06 {
    public static void main(String[] args) throws InterruptedException {
        //运用lambda表达式对方法进行简化
        Thread thread=new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(200);
                } 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);//RUN
        while(state!=thread.getState().TERMINATED)
        {
            thread.sleep(200);
            state = thread.getState();
            System.out.println(state);//RUN
        }
    }
}

image-20201031215226449

4、线程的优先级

4.1设置优先级并输出

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。

线程的优先级用数字表示,范围从1~10.

Thread.MIN_PRIORITY= 1;

Thread.MAX_PRIORITY= 10;

Thread.MAX_PRIORITY= 10;

getPriority() . setPriority(int xxx)

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

package SunThread;

public class ThreadPriority_01 {
    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);//Thread.MAX_PRIORITY=10
        t4.start();
        t5.setPriority(8);
        t5.start();
        t6.setPriority(7);
        t6.start();
    }
}
class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

image-20201031222746469

优先级低只是意味着获得调度的概率低.并不是优先级低就不会被调用了.这都是看CPU的调度

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

5、线程的守护

5.1代码解释线程的守护

1、线程分为用户线程和守护线程

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

3、虚拟机不用等待守护线程执行完毕

4、如,后台记录操作日志,监控内存,垃圾回收等待!.

package SunThread;

public class ThreadDaemon_01 {
    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("生活了"+i+"天");
        }
        System.out.println("goodbye world!");
    }
}

image-20201031230630145

6、线程的同步机制

6.1线程的同步理解

​ 由于我们可以通过private 关键字来保证数据对象只能被方法访问﹐所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块.

​ 同步方法:public synchronized void method(int args)0{}

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

缺陷:若将一个大的方法申明为synchronized将会影响效率

6.2线程的不安全性

用到lambda表达式简化https://blog.csdn.net/SUN__CGJ/article/details/109406652

package SunThread;

import java.util.ArrayList;

public class Thread_07 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            //lambda表达式简化
            new Thread(()->{list.add(Thread.currentThread().getName())}).start();
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

image-20201101100340495

期待输出9999,但是输出只有9994,这就是线程的不安全性。

6.3实现线程同步

同步块 :synchronized (Obj ){}

Obj称之为同步监视器
Obj可以是任何对象﹐但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器﹐因为同步方法的同步监视器就是this ,就是这个对象本身﹐或者是class [反射中讲解]
同步监视器的执行过程
1.第一个线程访问﹐锁定同步监视器﹐执行其中代码.

2第二个线程访问﹐发现同步监视器被锁定﹐无法访问.

3.第一个线程访问完毕﹐解锁同步监视器.
4.第二个线程访问,发现同步监视器没有锁,然后锁定并访问

package SunThread;

import java.util.ArrayList;

public class Thread_07 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            //lambda表达式简化
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

image-20201101101343864

这样就是与我们的期待值相符合了。简单理解就是加锁,锁的是所需要操作的对象。

补充:

运用JUC并发编程实现同步。

package SunThread;

import java.util.concurrent.CopyOnWriteArrayList;

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

image-20201101103950577

7、线程的死锁状态

7.1死锁

​ 多个线程各自占有一些共享资源﹐并且互相等待其他线程占有的资源才能运行﹐而导致两个或者多个线程都在等待对方释放资源﹐都停止执行的情形.某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题.

产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用。

2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

用两个小孩想吃苹果和香蕉进行解释

package SunThread;

public class DeadLock_01 {
    public static void main(String[] args) {
        Eat e1 = new Eat(0, "小明");
        Eat e2 = new Eat(2, "小红");
        e1.start();
        e2.start();

    }
}
class Apple{ }
class Banana{ }
class Eat extends Thread{
    static Apple apple=new Apple();
    static Banana banana=new Banana();
    int choice;
    String name;
     Eat(int choice,String name){
        this.choice=choice;
        this.name=name;
     }
    @Override
    public void run() {
        try {
            eating();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void eating() throws InterruptedException {
         if(choice==0){
             synchronized (apple){
                 System.out.println(this.name+"获得苹果的锁");
                 Thread.sleep(1000);
                 synchronized (banana){
                     System.out.println(this.name+"获得香蕉的锁");
                 }
             }

         }
         else{
             synchronized (banana){
             System.out.println(this.name+"获得香蕉的锁");
                 Thread.sleep(2000);
                 synchronized (apple){
                     System.out.println(this.name+"获得苹果的锁");
                 }
             }
         }
    }
}

image-20201101113257580

期待输出:

小明获得苹果的锁
小红获得香蕉的锁
小明获得香蕉的锁
小红获得苹果的锁

但是输出的不一样,而且程序在一直运行。

解决方法:

package SunThread;

public class DeadLock_01 {
    public static void main(String[] args) {
        Eat e1 = new Eat(0, "小明");
        Eat e2 = new Eat(2, "小红");
        e1.start();
        e2.start();

    }
}
class Apple{ }
class Banana{ }
class Eat extends Thread{
    static Apple apple=new Apple();
    static Banana banana=new Banana();
    int choice;
    String name;
     Eat(int choice,String name){
        this.choice=choice;
        this.name=name;
     }
    @Override
    public void run() {
        try {
            eating();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void eating() throws InterruptedException {
         if(choice==0){
             synchronized (apple){
                 System.out.println(this.name+"获得苹果的锁");
                
             }
             Thread.sleep(1000);
             synchronized (banana){
                 System.out.println(this.name+"获得香蕉的锁");
             }

         }
         else{
             synchronized (banana){
             System.out.println(this.name+"获得香蕉的锁");
                 
             }
             Thread.sleep(2000);
             synchronized (apple){
                 System.out.println(this.name+"获得苹果的锁");
             }
         }
    }
}

image-20201101113435853

7、Lock锁

​ 从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当

​ java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象

​ ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。

​ 买票的例子进行解释。

package SunThread;

import java.util.concurrent.locks.ReentrantLock;

public class TeskLock_01 {
    public static void main(String[] args) {
        TeskLock teskLock = new TeskLock();
        new Thread(teskLock, "小明").start();
        new Thread(teskLock, "小李").start();
        new Thread(teskLock, "小红").start();
    }
}
    class TeskLock implements Runnable {
        private int a = 10;
        //定义lock
        private final ReentrantLock lock = new ReentrantLock();
        @Override
        public void run() {
            while (true) {
                lock.lock();//加锁
                try {
                    if(a>0) {
                        Thread.sleep(200);
                        System.out.println(Thread.currentThread().getName() + "拿到了第" + a-- + "张票");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();//解锁
                }
            }
        }
    }


image-20201101115845129

符合常理。

8、生产者与消费者问题

8.1应用场景:生产者消费者问题

  • 1、假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费.

  • 2、如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止.

  • 3、如果产品中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库再次放入产品为止.

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

  • image-20201101191636901

8.2管程法进行实现

 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)

 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)

 缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区,生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

样例思路:

1.首先有一个生产者,消费者、生产者只顾生产,消费者只管消费、

2.利用了一个缓冲区,缓冲了一个10个大小的数组

3.有个方法叫放入产品,产品丢进来的时候,我们判断一下缓冲区有没有满,如果满了的话,生产者就要等待了,

如果没有满,就将产品放进去,放进去之后有产品了,赶紧通知消费者消费

4.消费者就判断下能不能消费呢,有没有东西,有东西的话,我就可以直接消费,消费完了,就赶紧通知生产者生产。

如果没有东西呢,消费者就等待。等待生产者去通知他,生产者通知了,他就可以解除等待了

package SunThread;
//测试: 生产者消费者模型-->利用缓冲区解决:管程法
//生产者 , 消费者 , 产品 , 缓冲区
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;
    }
}

image-20201101191044408

8.3信号灯法

​ 来判断一个标志位flag,如果为true,就让他等待、如果为false,就让他去通知另外一个人、把两人衔接起来,就像咱们的信号灯红灯停,绿灯行,通过这样一个判断方式,只要来判断什么时候让他等待,什么时候将他唤醒就ok。

package SunThread;

//测试生产者消费者问题2:信号灯法,通过标志位解决

public class TestPC_01 {
    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.notifyAll();
        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;
    }
}

image-20201101192234909

9、线程池

9.1用Runable实现线程池

  1. 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大

  2. 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中公共交通工具。

  3. 好处:

    提高响应速度(减少了创建线程的时间)

    降低资源的消耗(重复利用线程池中线程,不需要每次都创建)

    便于线程管理(...)

    corePoolSize:核心池的大小

    maximumPoolSize:最大线程数

    keepAliveTime:线程没有任务时最多保持多长时间后会终止

  4. JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors

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

  • void execute(Runnable command):执行任务/命令,没有返回值,一 般用来执行Runnable

  • Future submit(Callable task):执行任务,有返回值,一般用来执行Callable

  • void shutdown():关闭连接池

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

用Runable实现线程池

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

//测试线程池
public class TestPool {

    public static void main(String[] args) {
        //1.创建服务,创建线程池
        //newFixedThreadPool(10); 参数为线程池的大小
        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());
    }

}

自学习于哔哩哔哩狂神说JAVA(侵删);

作为自己的笔记,欢迎大家斧正!

posted @ 2020-11-02 00:02  -SUN-  阅读(677)  评论(0编辑  收藏  举报