4.14-4.16学习总结

news2024/11/17 6:00:13

多线程:

线程:

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

进程:

进程是程序的基本执行实体

举个例子:360运行之后,它就可以看做是一个进程,但运行我的电脑,木马查杀,电脑清理等功能时,就是线程。 

 

 可以同时运行多个功能就是多线程

 1.什么是多线程?
有了多线程,我们就可以让程序同时做多件事情
2.多线程的作用?
提高效率
3.多线程的应用场景?
只要你想让多个事情同时运行就需要用到多线程
比如:软件中的耗时操作、所有的聊天软件、所有的服务器

 并发和并行

并发和并行
并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行

并发:来回交替不断

并行:直接两个一起运行 

 多线程的实现方式

 (1)继承Thread类的方式进行实现

package homework;

public class ThreadDemo {
    public static void main(String[] args) {
        /**
         * 多线程的第一种启动方式
         * 1.自己定义一个类继承Thread
         * 2.重写 run 方法
         * 3.创建子类的对象,并启动线程
         *
         */


        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();


        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
    }
}


package homework;

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



//打印结果能发现运行线程1 和线程2 交织运行

(2)实现Runnable接口的方式进行实现

package homework;

public class ThreadDemo {
    public static void main(String[] args) {
        /**
         * 多线程的第二种启动方式
         * 1.自己定义一个类继承Thread
         * 2.重写 run 方法
         * 3.创建子类的对象,并启动线程
         * 4.创建一个Thread类的对象,并且开启线程
         */


        //创建MyRun的对对象
        //表示多线程要执行的任务
        MyRun mr = new MyRun();


        //创建线程对象
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);

        t1.setName("线程1");
        t2.setName("线程2");

        t1.start();
        t2.start();
    }
}


package homework;


public class MyRun implements Runnable{
    @Override
    public void run()
    {
        for(int i=0;i<100;i++)
        {
            //先获取到当前线程的对象
//            Thread t = Thread.currentThread();
//
//            System.out.println( t.getName() + "   Hello,world!");
            System.out.println(Thread.currentThread().getName() + "   Hello,world!");
        }
    }
}


//打印结果的时候依旧能看到线程的交织
//唯一的区别就是这一个方法运用的是线程的接口
//不能直接调用父类的获取名称方法
//需要先获取一个对象,通过对象再去调用方法

(3)利用Callable接口和Future接口方式实现

package homework;

import com.sun.corba.se.impl.orbutil.closure.Future;

import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) {
        /**
         * 多线程的第三种启动方式(是对前面两种方法的补充,前面的两种启动方法都没有返回值)
         * 特点:可以获取到多线程运行的结果
         * 1.创建一个类  MyCallable  实现  Callable 接口
         * 2.重写 call 方法 (是有返回值的,表示多线程运行的结果)
         * 3.创建 MyCallable 的对象(表示多线程要执行的任务)
         * 4.创建 FutureTask 的对象(作用管理多线程运行的结果)
         * 5.创建 Thread 类的对象,并启动(表示线程)
         *
         *
         */

        //创建 MyCallable 的对象(表示多线程要执行的任务)
        MyCallable mc = new MyCallable();
        //创建 FutureTask 的对象(作用管理多线程运行的结果)
        FutureTask<Integer> ft = new FutureTask<>(mc);

        //创建线程的对象
        Thread t1 = new Thread(ft);
        t1.start();
        try {
            Integer result = ft.get();
            System.out.println(result);
        }catch (Exception e)
        {
            e.printStackTrace();
            System.out.println("网络出现错误!");
        }
    }
}
package homework;

import javax.xml.ws.WebServiceException;
import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception
    {
        int sum = 0;
        for(int i=1;i<=100;i++)
        {
            sum = sum + i;
        }
        return sum;
    }
}

 如果想要获取结果,使用 Callable 接口重写方法可以获得返回值!

多线程的常用方法

 注意事项:

  • static Thread currentThread() 方法是静态方法(也就是类方法),可以直接调用
  • static void sleep(long time) 停留的时间由传入的参数决定
  • 在 Java 的线程当中,最小的优先级是 1 ,最大是 10 ,默认是中间的 5
  • 线程的优先级越大,抢占到  cpu  的概率越大

抢占式调度:随机性

非抢占式调度: 轮流执行

package homework;

import com.sun.corba.se.impl.orbutil.closure.Future;
import lambdatest.MyThread;

import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) {
       /*
         String getName()                  返回此线程的名称
         void setName(String name)         设置线程的名字(构造方法也可以)
            细节:
             1.如果我们没有为线程设置名字,线程也是有默认的名字的
                    格式:Thread-X(X是序号,序号从0开始)
             2.如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置


          static Thread currentThread()     获取当前线程的对象
          细节:
                当JVM虚拟机启动之后,会自动的启动多条线程
                其中有一条线程就叫做 main 线程
                他的作用就是去调用 main 方法,并执行里面的代码
                在以前,我们写的所有的代码,都是运行在  main  线程当中

          static void sleep(long time)      让线程休眠指定的时间,单位为:毫秒
          细节:
              1.哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
              2.方法的参数,就表示睡眠的时间,单位是:ms
                    1 s = 1000 ms
              3.当时间到了之后,线程会自动醒来,继续执行下面的其它代码
         */


        // 1.创建线程的对象
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        //2.开启线程
        t1.start();
        t2.start();
       /* 如果要创建自带名字的对象,要使用构造器
        但是 MyThread 是 Thread 的子类
        子类不能直接调用父类的方法
        必须在子类里面重写父类的方法*/
        MyThread t3 = new MyThread("飞机");
        MyThread t4 = new MyThread("坦克");




        //哪条线程执行到这个方法,此时获取的就是哪条线程的对象
        Thread t = Thread.currentThread();
        String name = t.getName();
        System.out.println(name);


        try {
            System.out.println("1111111111111");
            Thread.sleep(5000);
            System.out.println("2222222222222");
        }catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}


package lambdatest;

public class MyThread extends Thread{

    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run()
    {
        for(int i=0;i<100;i++)
        {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(getName() + " @ " + i);
        }
    }
}

Java 使用的抢占式调度,即优先级越高,抢占到 cpu 的概率更大

优先级代码:

通过不断运行结果可见,坦克先打印完100的次数大于飞机先打印完100的次数;也就是说,通过设定线程的优先级可以改变线程抢占到 cpu 的概率

package homework;

import com.sun.corba.se.impl.orbutil.closure.Future;
import lambdatest.MyRunnable;
import lambdatest.MyThread;

import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) {
        /*

        setPriority(int newPriority)            设置线程的优先级
        final int getPriority()                 获取线程的优先级


         */

        //创建线程要执行的参数对象
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr,"飞机");
        Thread t2 = new Thread(mr,"坦克");

        t1.setPriority(1);
        t2.setPriority(10);

        t1.start();
        t2.start();

    }
}

package lambdatest;

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

 守护线程:

当其他的非守护线程执行完毕之后,守护线程就会陆续结束。

通俗一点理解:其他线程是老大,守护线程是小弟,其他线程结束了(老大没了),小弟就没有必要存在了。

应用场景:例如QQ聊天的时候,聊天界面作为一个线程,传输文件作为一个线程,如果关闭了聊天界面,那么传输文件就没有继续下去的必要了

package homework;

import com.sun.corba.se.impl.orbutil.closure.Future;
import exericise.MyThread;
import exericise.MyThread2;
import lambdatest.MyRunnable;

import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) {
        /*

        setPriority(int newPriority)            设置线程的优先级
        final int getPriority()                 获取线程的优先级
        细节:
            当其他的非守护线程执行完毕之后,守护线程就会陆续结束
            可以理解为:可以认为守护线程是其他线程的备胎
                      其他线程都没有了
                      那么备胎线程也没有存在的必要了


         */
        MyThread t1 = new MyThread();
        MyThread2 t2 = new MyThread2();


        //将第二个线程设置为守护线程(也可以称备胎线程)
        t2.setDaemon(true);

        t1.setName("ice cream");
        t2.setName("milk tea");


        t1.start();
        t2.start();
    }
}



package exericise;

public class MyThread extends Thread{
    @Override
    public void run()
    {
        for(int i=1;i<=10;i++)
        {
            System.out.println(getName() + "   " + i );
        }
    }
}


package exericise;

public class MyThread2 extends Thread{
    @Override
    public void run()
    {
        for(int i=1;i<=100;i++)
        {
            System.out.println(getName() + " @ "  + i );
        }
    }
}

 礼让线程:

让目前抢占到 CPU 控制权的线程让出来,再让已创建的线程重新抢夺 CPU 的控制权

package homework;

import com.sun.corba.se.impl.orbutil.closure.Future;
import exericise.MyThread2;
import lambdatest.MyRunnable;

import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) {
        /*
              public static void yield()        出让线程/礼让线程

         */
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();


        t1.setName("ice cream");
        t2.setName("milk tea");


        t1.start();
        t2.start();
    }
}

package homework;

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

            //表示出让当前 CPU 的执行权
            //原本的线程在运行到下面这个代码会让出 CPU 的控制权
            //出让 CPU 的控制权后,已有的线程重新争夺 CPU 的控制权
            //这样让结果尽可能的均匀
            Thread.yield();

    
        }
    }
}

插入线程:

插入线程就是插队,让某个线程先执行完毕再让其他的执行

package homework;

import com.sun.corba.se.impl.orbutil.closure.Future;
import exericise.MyThread2;
import lambdatest.MyRunnable;
import lambdatest.MyThread;

import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) {
        /*
              public final void join()        插入线程/插队线程

         */

        MyThread t = new MyThread();
        t.setName("我是不重要的线程 o_o");
        t.start();

        //表示把 t 这个线程,插入到当前线程之前
        //t:我是不重要的线程 o_o 执行完毕之后才会执行 main 线程

        try {
            t.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }


        //执行在 main 线程当中
        for(int i=0;i<10;i++)
        {
            System.out.println("main 线程  " + i);
        }
    }
}



package exericise;

public class MyThread extends Thread{
    @Override
    public void run()
    {
        for(int i=1;i<=10;i++)
        {
            System.out.println(getName() + "   " + i );
        }
    }
}


package test;

public class MyThread extends Thread{

    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run()
    {
        for(int i=0;i<100;i++)
        {

            System.out.println(getName() + " @ " + i);
        }
    }
}

 线程的生命周期

sleep方法让线程睡眠,睡眠时间到了以后,线程需要变为就绪状态,直到抢到 CPU 才会执行代码!!!

线程的安全问题 

需求:
某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票


如果使用单纯的多线程,结果出现了一下的问题:

  1. 相同的票出现了多次
  2. 出现了超出范围的票
     

出现问题的原因:

线程执行的时候,有随机性,CPU 的控制权很有可能会被其他线程给抢走

同步代码块:

锁的特点:

  1. 锁默认打开,有一个线程进去了,锁自动关闭
  2. 里面的代码全部执行,线程出来,锁自动打开

使用同步代码块的时候要注意:

  • 使用this关键字可以锁定当前实例对象,即只有获得该实例对象的锁才能执行同步代码块。这意味着如果两个不同的线程分别调用相同的实例对象上的同步方法或同步代码块,它们不能同时执行。
  • 使用当前类的字节码文件作为锁对象:这意味着只有获得了该类的字节码的锁,才能执行同步代码块。这样就能避免不同线程之间调用不同实例对象的同步方法时互相干扰。
  • 在使用synchronized关键字时,如果需要同时控制多个不同实例对象上的同步方法或同步代码块,则应该使用当前类的字节码文件作为锁对象。如果需要控制单个实例对象的同步方法或同步代码块,则应该使用this关键字作为锁对象。

简单来说:类的字节码指向的是类,而  this 关键字指向的是实例

package homework;

import com.sun.corba.se.impl.orbutil.closure.Future;
import exericise.MyThread2;
import lambdatest.MyRunnable;

import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) {

        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();


        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

package homework;

public class MyThread extends  Thread{

    static int ticket = 0;


    @Override
    public void run(){
        while(true)
        //同步代码块
        {
            synchronized (MyThread.class)
            {
                if(ticket<100)
                {
//                    try {
//                        Thread.sleep(100);
//                    } catch (InterruptedException e) {
//                        throw new RuntimeException(e);
//                    }
                    ticket++;
                    System.out.println(getName() + "正在出售第" + ticket +"张票!" );
                }
                else
                {
                    break;
                }
            }

        }
    }
}

同步方法

package homework;

import com.sun.corba.se.impl.orbutil.closure.Future;
import exericise.MyThread;
import exericise.MyThread2;
import lambdatest.MyRunnable;

import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) {
        /*
        利用同步方法完成
        技巧:先写同步代码块,再改成同步方法

         */
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        Thread t3 = new Thread(mr);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");


        t1.start();
        t2.start();
        t3.start();

    }
}



package lambdatest;

public class MyRunnable implements Runnable{
    int ticket = 0;

    //1.循环
    //2.同步代码块(同步方法)
    //3.判断共享数据是否到了末尾,如果到了末尾
    // 4.判断共享数据是否到了末尾,如果没有到末尾

    @Override
    public void run()
    {
        while (true)
        {
          synchronized (MyRunnable.class)
          {
              if(method()) break;
          }
        }

    }

    private synchronized boolean method()
    {
        if(ticket == 100)
        {
            return false;
        }
        else
        {
            ticket++;
            System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票!!!");
        }
        return false;
    }
}

 手动锁:

 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作Lock中提供了获得锁和释放锁的方法
void lock():获得锁
void unlock():释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例

package homework;

import com.sun.corba.se.impl.orbutil.closure.Future;
import exericise.MyThread2;
import lambdatest.MyRunnable;

import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) {
        /*
        利用同步方法完成
        技巧:先写同步代码块,再改成同步方法

         */
//        MyRunnable mr = new MyRunnable();
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");


        t1.start();
        t2.start();
        t3.start();

    }
}

package homework;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread extends  Thread{

    static int ticket = 0;
    static Lock lock = new ReentrantLock();

    @Override
    public void run(){
        while(true)
        //同步代码块
        {
            lock.lock();
            try {
                if(ticket==100)
                {
                    break;
                }
                else
                {
                    ticket++;
                    System.out.println(getName() + "正在出售第" + ticket +"张票!" );
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

 死锁:

死锁是错误!!!死锁是锁的嵌套,写程序的时候要防止死锁的错误。

死锁产生的原因主要可以归结为以下四个必要条件:

  1. 互斥条件:每个资源同时只能被一个进程持有或使用。
  2. 请求和保持条件:一个进程因请求资源而阻塞时,已经获得的资源保持不放。
  3. 不剥夺条件:已经分配给某个进程的资源不能被其他进程强制性地抢占,只能由该进程自行释放。
  4. 环路等待条件:存在一种进程资源的环形等待链,链中的每个进程都在等待下一个进程所持有的资源。

为了避免死锁,应该采取以下措施:

  1. 避免一次性请求过多资源,实现资源按需获取。
  2. 避免持有资源时阻塞请求其他资源。
  3. 尽量避免循环等待,在必要时对资源使用顺序进行限制。
  4. 设置资源超时时间,及时释放长期占用的资源。
  5. 使用同步模块来确保线程不会在临界区中互相等待。

例如上面这个代码:由于编码时产生了锁的嵌套,导致A、B锁都在等对方解锁而导致程序无法进行下去而产生死锁。 

生产者和消费者(等待唤醒机制)
生产者消费者模式是一个十分经典的多线程协作的模式

 

package homework;

import exericise.Cook;
import exericise.Foodie;

public class ThreadDemo{
    public static void main(String [] args)
    {
        Cook c= new Cook();
        Foodie f = new Foodie();


        c.setName("厨师");
        f.setName("吃货");


        c.start();
        f.start();
    }
}


public class Cook extends Thread {
    @Override
    public void run()
    {
         /*
        1.循坏
        2.同步代码开
        2.判断共享数据是否到了末尾(到了末尾)
        2.判断共享数据是否到了末尾(没有到位末尾,执行核心逻辑)
         */

        while(true)
        {
            synchronized (Desk.lock)
            {
                if(Desk.count == 0)
                {
                    break;
                }
                else
                {
                    if(Desk.foodFlag==1)
                    {
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.getStackTrace();
                        }
                    }
                    else {
                        System.out.println("厨师做了一碗面条");
                        Desk.foodFlag = 1;
                        Desk.lock.notifyAll();
                    }
                }
            }
        }
    }
}
package exericise;

public class Foodie extends Thread{
    @Override
    public void run()
    {
        /*
        1.循坏
        2.同步代码开
        2.判断共享数据是否到了末尾(到了末尾)
        2.判断共享数据是否到了末尾(没有到位末尾,执行核心逻辑)
         */
        while(true)
        {
            synchronized (Desk.lock)
            {
                if(Desk.count == 0)
                {
                    break;
                }
                else
                {
                    //判断桌子上是否有面条
                    //如果没有,就等待
                    //如果有,就开吃
                    //吃完之后,要唤醒厨师继续做
                    //面条数量-1
                    //修改桌子的状态,就是有误面条的情况判断
                    if(Desk.foodFlag == 0)
                    {
                        try {
                            Desk.lock.wait();//让当前线程跟锁进行绑定
                        } catch (InterruptedException e) {
                            e.getStackTrace();
                        }
                    }else {
                        Desk.count--;
                        System.out.println(getName() + "正在吃面条,能继续吃" + Desk.count + "碗") ;
                        Desk.lock.notify();
                        Desk.foodFlag = 0;
                    }
                }
            }
        }
    }
}package exericise;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Desk {
    /*
    控制生产者和消费者的执行
     */

    //桌子上时候有面条: 0:没有面条 1:有面条
    public static int foodFlag=0;

    //总个数:
    public static int count = 10;

    //锁对象
    public static Object lock = new Object();
}

 

package homework;

import exericise.Cook;
import exericise.Foodie;

import java.util.concurrent.ArrayBlockingQueue;

public class ThreadDemo{
    public static void main(String [] args)
    {
        ArrayBlockingQueue<String >  queue = new ArrayBlockingQueue<>(1);
     

        Cook c= new Cook(queue);
        Foodie f = new Foodie(queue);


        c.setName("厨师");
        f.setName("吃货");


        c.start();
        f.start();
    }
}



package exericise;

import java.util.concurrent.ArrayBlockingQueue;

public class Foodie extends Thread {
    ArrayBlockingQueue<String> queue;

    public Foodie(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                String food = queue.take();
                //take 方法本身也有锁
                System.out.println(food);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

线程的状态

 

Java中是没有定义运行状态的, 一旦抢占到 cpu ,Java虚拟机就会将执行交给操作系统

 

 线程池:

  1. 创建一个池子,池子中是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待

package homework;

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

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        /*
        public static ExecutorService newCachedThreadPool()                 
        创建一个没有上线的线程池
  

         */

        ExecutorService pool1 = Executors.newCachedThreadPool();

        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());

//        pool1.shutdown();

    }
}


package homework;

public class MyRunnable implements  Runnable{
    @Override
    public void run()
    {
        for(int i=0;i<100;i++)
        {
            System.out.println(Thread.currentThread().getName() + " 正在运行  " + i);
        }
    }
}
package homework;

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

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        /*

        public static ExecutorService newFixedThreadPool (int nThreads)     
        创建有上限的线程池

         */

        ExecutorService pool1 = Executors.newFixedThreadPool(3);
        //定义了上限之后,能看到打印结果只有3个线程

        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());

//        pool1.shutdown();

    }
}


package homework;

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/419821.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【UE4】关卡流送的demo

关卡流送功能可以将地图文件加载到内存中&#xff0c;或者从内存中卸载&#xff0c;并在游戏过程中切换地图的可视性。 这样一来&#xff0c;场景便能拆分为较小的地图块&#xff0c;并且只有相关部分才会占用资源并被渲染。 正确设置后&#xff0c;开发者便能创建大型、无缝衔…

c++STL容器之序列式容器

目录 vector容器 vector对象的默认构造 vector的初始化 vector的遍历 vector的增删改查 vector末尾的添加移除操作 vector的数据存取 deque容器 deque对象的默认构造 deque末尾的添加移除操作 deque的数据存取 stack容器 stack对象的默认构造 stack的push()与po…

Midjourney科普介绍

Midjourney是啥&#xff1f; Midjourney是一个由Midjourney研究实验室开发的人工智能程序&#xff0c;可根据文本生成图像&#xff0c;于2022年7月12日进入公开测试阶段&#xff0c;使用者可通过Discord的机器人指令进行操作&#xff0c;可以创作出很多的图像作品。 以下这些…

ROS2中将octomap发布到Moveit中作为碰撞物体

1.安装准备 这里假设你已经装好了ROS2以及Moveit2&#xff08;都用二进制安装就行&#xff0c;不用从源码安转&#xff09;&#xff0c;没有安装好的&#xff0c;可以按照鱼香ROS的教程安装&#xff0c;两三行命令就搞定了。 我的ROS2版本为humble&#xff0c;请根据你使用的实…

银行数字化转型导师坚鹏:银行同业核心产品与营销策略解读

数字化背景下银行同业核心产品与营销策略解读课程背景&#xff1a; 数字化背景下&#xff0c;很多银行存在以下问题&#xff1a; 不清楚银行同业核心产品发展现状&#xff1f; 不清楚如何银行同业产品营销策略&#xff1f; 不知道如何更好地挖掘他行优质客户&#xff1f; 课…

国产化系统改造实践(未完)

一、项目背景 2020 年,红帽公司宣布,将在 2021 年 12 月 31 日和 2024 年 6 月 30 日分别终止对 CentOS 8 和 CentOS 7 的服务支持,把 CentOS 项目的工作和投资集中在CentOS Stream 上。 CentOS Linux 8已于2021年12月31日停止维护,CentOS Linux7也 将于2024年6月停服。s所…

技术创业者必读:从验证想法到技术产品商业化的全方位解析

导语 | 技术创业之路往往充满着挑战和不确定性&#xff0c;对于初入创业领域的人来说&#xff0c;如何验证自己的创业想法是否有空间、如何选择靠谱的投资人、如何将技术产品商业化等问题都需要认真思考和解决。在「TVP 技术夜未眠」第六期直播中&#xff0c;正马软件 CTO、腾讯…

【《C Primer Plus》读书笔记】第17章:高级数据表示

【《C Primer Plus》读书笔记】第17章&#xff1a;高级数据表示17.1 研究数据表示17.2 从数组到链表17.3 抽象数据类型&#xff08;ADT&#xff09;17.4 队列ADT17.5 用队列进行模拟17.6 链表和数组17.7 二叉查找树17.8 其他说明17.1 研究数据表示 在开始编写代码之前&#xf…

【2023】前端JWT详解

概述 回顾登录的流程&#xff1a; 接下来的问题是&#xff1a;这个出入证&#xff08;令牌&#xff09;里面到底存啥&#xff1f; 一种比较简单的办法就是直接存储用户信息的JSON串&#xff0c;这会造成下面的几个问题&#xff1a; 非浏览器环境&#xff0c;如何在令牌中记录…

C/C++每日一练(20230416)

目录 1. 求数列第n项值 ※ 2. 整数转换英文表示 &#x1f31f;&#x1f31f;&#x1f31f; 3. 数组中找出最大值及索引位置 ※ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 求数…

微信小程序引入骨架屏

骨架屏的使用越来越广泛。在微信小程序中使用骨架屏如果自己实现&#xff0c;不同的页面对应不同的骨架屏&#xff0c;会有一定难度&#xff1b;不过&#xff0c;微信小程序已经提供生成骨架屏功能&#xff0c;使得我们在开发中非常方便&#xff0c;下面介绍如何生成 在生成骨架…

[Linux]管理用户和组

​⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;Linux基础操作。本文主要是分享一些Linux系统常用操作&#xff0c;内容主要来源是学校作业&#xff0c;分享出来的…

JavaEE 协议 信息是如何在一个机器传到另一个机器的(理论)

抓住你了&#xff01; 文章目录JavaEE & 协议 & 信息是如何在一个机器传到另一个机器的1. 局域网2. 广域网3. IP与端口号&#xff08;初识&#xff09;4. 协议4.1 协议分类分层4.2 协议分层的好处4.3 真实的网络协议分层&#xff0c;TCP/IP五层网络模型4.3.1 应用层4.3…

Spark 3.0中 Spark SQL优化

在Spark3.x版本提供Adaptive Query Execution自适应查询技术&#xff0c;通过在”运行时”对查询执行计划进行优化&#xff0c;允许Planner在运行时执行可选计划&#xff0c;这些可选计划将会基于运行时数据统计进行动态优化&#xff0c;从而提高性能。 Adaptive Query Execut…

在DongshanPI-D1开箱使用分享与折腾记录实现MPU6050数据读取

前言 上一篇文章使用RT-Smart的IIC驱动OLED屏幕&#xff0c;进行基本的字符串显示,在使用过程中对RT-Smart有了一定熟悉&#xff0c;准备使用SPI驱动ST7789&#xff0c;但SPI接口没有引出&#xff0c;本次使用手上已有的传感器MPU6050进行使用。 过程 本次直接开始添加离线包…

NDK RTMP直播客户端二

在之前完成的实战项目【FFmpeg音视频播放器】属于拉流范畴&#xff0c;接下来将完成推流工作&#xff0c;通过RTMP实现推流&#xff0c;即直播客户端。简单的说&#xff0c;就是将手机采集的音频数据和视频数据&#xff0c;推到服务器端。 接下来的RTMP直播客户端系列&#xff…

在国内pmp证书有什么含金量?

关于PMP的含金量&#xff0c;很多回答的说法都差不多&#xff0c;但那也只是字面上的含金量&#xff0c;真正的含金量还是得自己考过了之后能够给自己带来的帮助才方可对PMP含金量下定义&#xff0c;但能一眼就能看到的含金量是在一些招聘信息上关于PMP证书的要求&#xff0c;下…

【Axure教程】日期时间下拉列表

在系统中&#xff0c;我们经常会用到日期时间选择器&#xff0c;它同时包含了日历日期的选择和时间的选择&#xff0c;一般是下拉列表的形式进行选择。 今天作者就教大家如何在Axure中用中继器制作真实日期时间效果的下拉列表选。 一、效果展示 1、点击控件&#xff0c;可以…

游戏开发学习路线图(2023最新版)建议收藏

游戏开发是一个高度技术化的领域&#xff0c;需要掌握编程语言和编程技能。你可以从学习基本的编程概念和语法开始&#xff0c;如C、C#、Python等常用的游戏编程语言。掌握编程的基础知识是游戏开发的基石。很多小伙伴不知道怎么学习游戏开发&#xff0c;那么今天&#xff0c;就…

c/c++:windows平台下依赖的动态库,c底层是汇编语言,程序断点调试,反汇编,vs快捷键

c/c&#xff1a;windows平台下依赖的动态库&#xff0c;c底层是汇编语言&#xff0c;程序断点调试&#xff0c;反汇编&#xff0c;vs快捷键 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;此时学会c的话&#xff0c; 我所知…