多线程..

线程定义:线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中实际运作单位。简单来说,应用软件中相互独立,可以同时运作的功能。

多线程作用:有了多线程,我们就可以让程序同时做多件事情。

应用场景:只要你想让多个事件同时运行就需要用到多线程。

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

并行:在同一时刻,有多个指令在多个CPU上同时执行。

多线程的实现方式

1.继承Thread类的方式进行执行

步骤:

自己定义一个类继承Thread

重写run方法

创建子类的对象,并启动线程。

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

}
public class test {
    public static void main(String [] args)  {
    	//创建子类对象,开启线程
    	MyThread mt=new MyThread();
    	mt.start();
	 }
}

2.实现Runnable接口的方式进行实现

步骤:

自己定义一个类实现Runnable接口

重写里面的run方法

创建自己类的对象

创建一个Thread类的对象,并开启线程。

public class MyThread implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println("world");
		}
	}

}
public class test {
    public static void main(String [] args)  {
    	//创建自己类的对象
    	MyThread mt=new MyThread();
    	//创建Thread对象,并开启线程
    	Thread t=new Thread(mt);
    	t.start();
	 }
}

 

若想看看两个线程的相互执行,获取线程的名字:

public class MyThread implements Runnable{
	//在此获取线程的名字
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
		Thread t=Thread.currentThread();//获取当前线程对象
		System.out.println(t.getName()+":world");
		}
	}

}
public class test {
    public static void main(String [] args)  {
    	//创建自己类的对象
    	MyThread mt=new MyThread();
    	//创建Thread对象,并开启线程
    	Thread t1=new Thread(mt);
    	Thread t2=new Thread(mt);
    	//给线程设置名字
    	t1.setName("线程1");
    	t2.setName("线程2");
    	t1.start();
    	t2.start();
	 }
}

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

特点:可以获取多线程运行的结果。

1)创建一个MyCallable类实现Callable接口;

2)重写call方法(是有返回值的,表示多线程运行的结果);

3)创建MyCallable的对象(表示多线程要执行的任务);

4)创建FutureTask的对象(作用管理多线程的运行结果);

5)创建Thread类的对象,并启动(表示线程)。

public class MyCallable implements Callable<Integer>{
    //计算1~10的和
	@Override
	public Integer call() throws Exception {
		int sum=0;
		for(int i=0;i<=10;i++) {
			sum+=i;
		}
		return sum;
	}

}
public class test {
    public static void main(String [] args) throws InterruptedException, ExecutionException  {
    	MyCallable mc=new MyCallable();
    	FutureTask<Integer> ft=new FutureTask<>(mc);//管理多线程运行结果
    	Thread t=new Thread(ft);
    	t.start();//开启线程
    	//获取返回值
         Integer sum=ft.get();
         System.out.println(sum);
	 }
}

多线程的成员方法

获取与设置线程的名字

注:若我们未给线程设置名字,线程也有默认的名字,格式:Thread-X(X为序号,从0开始)。

若我们要给线程设置名字,可以用set方法,也可以用构造方法(调用父类的构造方法)设置。

1)用set进行设置。

public class MyThread extends Thread{
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			//获取线程名字
			System.out.println(getName()+":"+i);
		}
	}

}

public class test {
    public static void main(String [] args)  {
    	MyThread t1=new MyThread();
    	MyThread t2=new MyThread();
    	//设置线程名字,用set方法设置
    	t1.setName("线程1");
    	t2.setName("线程2");
    	t1.start();
    	t2.start();
	 }
}

线程之间交替运行。 

2)用构造方法进行设置

public class MyThread extends Thread{
	public MyThread() {//调用父类的构造方法
		
	}
	public MyThread(String name) {
		super(name);
	}
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(getName()+":"+i);
		}
	}

}
public class test {
    public static void main(String [] args)  {
    	MyThread t1=new MyThread("飞机");
    	MyThread t2=new MyThread("鼠标");
    	t1.start();
    	t2.start();
	 }
}

线程休眠

static void sleep(long time)——让线程休眠指定时间,单位毫秒。

注:哪条线程执行到这个方法,那么哪条线程就会在这停留对应的时间。当时间到了之后,线程会自动醒来,执行下面的代码。

public class MyThread extends Thread{
	public MyThread() {//调用父类的构造方法
		
	}
	public MyThread(String name) {
		super(name);
	}
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			try {
				Thread.sleep(1000);//每个一秒打印一个数据
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(getName()+":"+i);
		}
	}

}
public class test {
    public static void main(String [] args)  {
    	MyThread t1=new MyThread("飞机");
    	MyThread t2=new MyThread("鼠标");
    	t1.start();
    	t2.start();
	 }
}

线程的优先级

在java中使用抢占式调度,其特点为随机性。优先级为1~10,默认优先级为5,优先级越高,抢到CPU的概率越大,但并不是百分之一百抢到CPU。

public class MyThread extends Thread{
	@Override
	public void run() {
		for(int i=0;i<7;i++) {
			System.out.println(getName()+":"+i);
		}
	}
}
public class test {
    public static void main(String [] args)  {
    	MyThread t1=new MyThread();
    	MyThread t2=new MyThread();
    	//设置线程名字
    	t1.setName("键盘");
    	t2.setName("鼠标");
    	//设置线程的优先级
    	t1.setPriority(1);
    	t2.setPriority(10);
    	t1.start();
    	t2.start();
	 }
}

鼠标优先级大,鼠标先执行完毕。

守护线程

当其他非守护线程结束后,守护线程也会陆续结束(一般不会执行完毕)。

public class MyThread1 extends Thread{
	@Override
	public void run() {
		for(int i=0;i<8;i++) {
			System.out.println(getName()+":"+i);
		}
	}
}
public class MyThread2 extends Thread{
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println(getName()+":"+i);
		}
	}
}
public class test {
    public static void main(String [] args)  {
    	MyThread1 t1=new MyThread1();
    	MyThread2 t2=new MyThread2();
    	//设置线程名字
    	t1.setName("线程");
    	t2.setName("备份");
    	//将t2设置为守护线程
    	t2.setDaemon(true);
    	t1.start();
    	t2.start();
	 }
}

礼让线程

礼让线程也叫出让线程,作用:尽可能使结果均匀一点。

public class MyThread extends Thread{
	@Override
	public void run() {
		for(int i=0;i<8;i++) {
			System.out.println(getName()+":"+i);
			Thread.yield();//出让线程
		}
	}
}
public class test {
    public static void main(String [] args)  {
    	MyThread t1=new MyThread();
    	MyThread t2=new MyThread();
    	//设置线程名字
    	t1.setName("线程1");
    	t2.setName("线程2");
    	
    	t1.start();
    	t2.start();
	 }
}

插入线程

public class MyThread extends Thread{
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println(getName()+":"+i);
		}
	}
}
public class test {
    public static void main(String [] args) throws InterruptedException  {
    	MyThread t1=new MyThread();	
    	t1.setName("土豆");
    	t1.start();
    	t1.join();//插入线程,将土豆线程插入到main线程之前
    	for(int i=0;i<10;i++) {
    		System.out.println("main线程"+i);
    	}
	 }
}

线程的生命周期

同步代码块

把操作共享的代码锁起来。

锁对象一定要求是唯一的。

 利用同步代码块可解决线程安全问题。若没有锁,可能会出现数据重复,数据超出要求的范围等等问题。

练习

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

public class MyThread extends Thread{
	//设置一个静态数据,表示这个类所有对象可以共享
	  static int ticket=0;//票数
	  static Object obj=new Object();//锁对象一定要是唯一的
	@Override
	public void run() {
		while(true) {
			synchronized(obj) {
			if(ticket<100) {
				try {
					Thread.sleep(100);//数据慢慢的输出
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				ticket++;
				System.out.println(getName()+"今天卖出第"+ticket+"票");
			}else {
				break;
			}
			}
		}
	}
}
public class test {
    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();
	 }
}

同步方法

就是把synchronized关键字加到方法上。

同步方法是用同步代码块提取方法,改编而来的。

练习

某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票。(使用同步方法完成,技巧:先构建好同步代码块,在进行变换)

public class MyThread implements Runnable{//使用第二种方法
    int ticket=0;//使用第二种方法创建对象,不用设置这个为静态的
    @Override
    public void run() {
        while(true) {       
                if (mehod()) break;         
        }

    }
    private synchronized boolean mehod() {//同步方法
        if(ticket==100) {
            return true;
        }else {
            try {
                Thread.sleep(100);//若想要看到多个线程交织进行,可采用sleep让线程睡一会
            } catch (final InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            ticket++;
            System.out.println(Thread.currentThread().getName()+"今天卖了第"+ticket+"张票");
        }
        return false;
    }


}
public class test1 {
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        Thread t1=new Thread(mt);
        Thread t2=new Thread(mt);
        Thread t3=new Thread(mt);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

Lock锁


 练习

某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票。(利用Lock锁进行)

在Lock锁当中利用try-catch-finall来进行开锁关锁。,将while循环中的主体程序写完之后,从获得锁开始后利用快捷方法生成try-catch-finall,finally中写释放锁。

public class MyThread extends Thread{
    //设置一个静态数据,表示这个类所有对象可以共享
    static int ticket=0;
    Lock lock=new ReentrantLock();//创建一个锁对象
    @Override
    public void run() {
        while(true) {
          //  synchronized (MyThread.class) {
            lock.lock();//获得锁
            try {
                if (ticket < 100) {
                        Thread.sleep(100);//数据慢慢的输出
                    ticket++;
                    System.out.println(getName() + "今天卖出第" + ticket + "票");
                } else {
                    break;
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();//释放锁
            }
            // }
        }
    }
}
public class test1 {
    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();
    }
    }

书写进程步骤:

生产者和消费者(等待唤醒机制)

生产者消费者模式是一个十分经典的多线程协作的模式。

例如:吃货和厨师(消费者和生产者)

常见方法:

实现方法1:基本方法 

 有:生产者、消费者、控制生产者和消费者的代码

先写中间:

public class Desk {//中间变量
    //作用:控制消费者和生产者的代码
    //定义桌子状态,0无面条,1有面条
    public static int state=0;
    //定义吃货最大能吃多少,总个数
    public static int count=10;
    //定义锁对象
    public static Object lock=new Object();
}

消费者:

public class Eat extends Thread{//消费者
    //循环
    //同步代码块(同步方法,锁方法均可)
    //判断共享数据是否到末尾(先写到了末尾,再写未到末尾)
    @Override
    public void run(){
        while(true){
            synchronized (Desk.lock){//锁对象
                //判断共享数据是否到末尾
                if(Desk.count==0){
                    break;
                }else{
                    //先判断桌子上物品状态
                    if(Desk.state==0){
                        //如果没有消费者等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else{
                        //如果有
                        Desk.count--;
                        System.out.println("这个人还能再吃"+Desk.count+"碗饭");
                        //吃完唤醒厨师继续做
                        Desk.lock.notifyAll();
                        //修改桌子状态
                        Desk.state=0;
                    }

                }
            }
        }
    }
}

生产者:

public class Cook extends Thread{//生产者
    @Override
    public void run(){
        while(true){
            synchronized (Desk.lock){
                if(Desk.count==0){
                    break;
                }else{
                    //判断桌子状态
                    if(Desk.state==1){
                        //如果有,就等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else{
                        //如果没有
                        System.out.println("厨师做了一碗面");
                        //修改桌子状态
                        Desk.state=1;
                        //唤醒消费者
                        Desk.lock.notifyAll();
                    }
                }
            }
        }
    }
}

测试类:

public class test1 {
    public static void main(String[] args) {
        Cook c=new Cook();
        Eat e=new Eat();
        //设置名字
        c.setName("厨师:");
        e.setName("吃货");
        //开启线程
        c.start();
        e.start();
    }
    }

实现方法2:阻塞队列方法实现

 阻塞队列的接口和实现类:

public class Cook extends Thread{//生产者
    ArrayBlockingQueue<String> queue;

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

    @Override
    public void run(){
        while(true){
            //调用阻塞队列将面条放入
            try {
                queue.put("面条");
                System.out.println("厨师放了一碗面条");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class Eat extends Thread{//消费者
    ArrayBlockingQueue<String> queue;

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

    @Override
    public void run(){
        while(true){
            try {
                String food=queue.take();
                System.out.println(food);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }


        }
    }
}
public class test1 {
    public static void main(String[] args) {
      //创建阻塞队列
        ArrayBlockingQueue<String> queue=new ArrayBlockingQueue<>(10);//定义阻塞队列中的队列长度
        Cook c=new Cook(queue);
        Eat e=new Eat(queue);
        c.setName("厨师");
        e.setName("吃货");
        c.start();
        e.start();
    }
    }

线程的六种状态

综合练习

练习1:抢红包

(因为获取Double类型的随机数只能在JDK17中,抢红包实际抢到的是小数,在此题下理想化,认为是整数)
抢红包也用到了多线程。假设: 100块,分成了3个包,现在有5个人去抢。其中,红包是共享数据。5个人是5条线程。
打印结果如下:
XXX抢到了XXX元
XXX抢到了XXX元
XXX抢到了XXX元
XXX没抢到
XXX没抢到

package test1;
import java.util.Random;
public class MyThread extends  Thread{
    //定义静态变量
    static  int money=100;//总金额
    static int count=3;//红包个数
    static final int MIN=1;//抢到的金额的最小值
    @Override
    public void run() {
        synchronized (MyThread.class) {
            //判断金额
            if (money == 0) {
                System.out.println(getName() + "没有抢到红包");
            } else {
                //判断红包个数
                int prize = 0;//中奖金额
                if (count == 1) {
                    //只剩一个红包,中奖金额就是余额
                    prize = money;
                } else {
                    //进行随机数,获取红包金额
                    Random r = new Random();
                    int bound = money - (count - 1) * MIN;
                    prize = r.nextInt(bound);
                    if (prize < MIN) {
                        prize = MIN;
                    }
                }
                money = money - prize;
                count--;
                System.out.println(getName() + "抢到了" + prize + "元");
            }
        }
    }
}
public class test1 {
    public static void main(String[] args) {
        MyThread t1=new MyThread();
        MyThread t2=new MyThread();
        MyThread t3=new MyThread();
        MyThread t4=new MyThread();
        MyThread t5=new MyThread();
        t1.setName("张三");
        t2.setName("李四");
        t3.setName("王五");
        t4.setName("赵六");
        t5.setName("导航");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
    }

练习2:抽奖池抽奖

有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为{10,5,20,50,100,200,500,800,2,80,300,700};
创建两个抽奖箱(线程)设置线程名称分别为“ 抽奖箱1”,‘ '抽奖箱2”随机从抽奖池中获取奖项元素并打印在控制台上格式如下:
每次抽出一个奖项就打印一个(随机)
抽奖箱1又产生了一个10元大奖
抽奖箱1又产生了一个100元大奖
抽奖箱2又产生了一个700元大奖
分析:可用集合和数组进行,但数组去重比较复杂,利用集合完成。

public class MyThread extends  Thread {
    ArrayList<Integer> list;//静态变量

    //可以利用构造方法
    public MyThread(ArrayList<Integer> list) {
        this.list = list;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (MyThread.class) {
                if (list.size() == 0) {
                    break;
                } else {
                    Collections.shuffle(list);//打乱list数据
                    int prize = list.remove(0);
                    System.out.println(getName() + "又产生了一个" + prize + "大奖");
                }
            }
            try {
                Thread.sleep(100);//目的是可以让两个线程交替进行显示
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }
    }
}
public class test1 {
    public static void main(String[] args) {
        ArrayList<Integer> list=new ArrayList<>();
        Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
        MyThread t1=new MyThread(list);
        MyThread t2=new MyThread(list);
        t1.setName("抽奖箱1");
        t2.setName("抽奖箱2");
        t1.start();
        t2.start();
    }
    }

练习3:多线程统计并求最大值

在上一题基础上继续完成如下需求:
每次抽的过程中,不打印,抽完时一次性打印(随机),在此次抽奖过程中,抽奖箱1总共产生了6个奖项。
分别为: 10,20,100,500,2,300最高奖项为300元,总计额为932元在此次抽奖过程中,抽奖箱2总共产生了6个奖项。
分别为: 5, 50,200,800,80,700最高奖项为800元,总计额为1835元

解法一:对第一个程序进行改编
 

public class MyThread extends  Thread{
    ArrayList<Integer> list;//静态变量
    //可以利用构造方法
    public MyThread(ArrayList<Integer> list){
        this.list=list;
    }
    //创建两个集合来存放数据
    static  ArrayList<Integer> list1=new ArrayList<>();
   static ArrayList<Integer> list2=new ArrayList<>();
    @Override
    public void run(){
        while(true){
          synchronized (MyThread.class) {
            if(list.size()==0){
                if("抽奖箱1".equals(getName())){
                    System.out.println("抽奖箱1"+list1);
                    int max=0;
                    int count=0;
                    for(int i=0;i<list1.size();i++){
                        if(max<= list1.get(i)){
                            max=list1.get(i);
                        }
                        count+=list1.get(i);
                    }
                    System.out.println("抽奖箱1最大奖"+max);
                    System.out.println("抽奖箱1总计"+count);
                }else if("抽奖箱2".equals(getName())) {
                    System.out.println("抽奖箱2"+list2);
                    int max=0;
                    int count=0;
                    for(int i=0;i<list2.size();i++){
                        if(max<= list2.get(i)){
                            max=list2.get(i);
                        }
                        count+=list2.get(i);
                    }
                    System.out.println("抽奖箱2最大奖"+max);
                    System.out.println("抽奖箱2总计"+count);
                }
                break;
            }else{
                Collections.shuffle(list);//打乱list数据
                int prize=list.remove(0);
               //进行判断
                if("抽奖箱1".equals(getName())){
                    list1.add(prize);
                }else if("抽奖箱2".equals(getName())){
                    list2.add(prize);
                }
            }
        }
}
        try {
            Thread.sleep(10);//目的是可以让两个线程交替进行显示
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

}

解法二:线程栈

public class MyThread extends  Thread {
    ArrayList<Integer> list;//静态变量

    //可以利用构造方法
    public MyThread(ArrayList<Integer> list) {
        this.list = list;
    }

    @Override
    public void run() {
        ArrayList<Integer> List=new ArrayList<>();
        while (true) {
            synchronized (MyThread.class) {
                if (list.size() == 0) {
                    System.out.println(getName()+List);
                    break;
                } else {
                    Collections.shuffle(list);//打乱list数据
                    int prize = list.remove(0);
                    List.add(prize);
                }
            }
            try {
                Thread.sleep(100);//目的是可以让两个线程交替进行显示
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }
    }
}

练习4:多线程之间的比较

在上一题基础上继续完成如下需求:在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为: 10,20,100,500,2,300,最高奖项为300元,总计额为932元
在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为: 5,50,200,800,80,700
最高奖项为800元,总计额为1835元
在此次抽奖过程中,抽奖箱2中产生了最大奖项,该奖项金额为800元
分析:最大奖项应该这两个线程结束后再比较,采用有返回值的线程创建方法,将最大值返回后打印。

public class MyCallable implements Callable<Integer> {
    ArrayList<Integer> list;//静态变量

    //可以利用构造方法
    public MyCallable(ArrayList<Integer> list) {
        this.list = list;
    }

    @Override
    public Integer call() throws Exception {
        ArrayList<Integer> List=new ArrayList<>();
        while (true) {
            synchronized (MyCallable.class) {
                if (list.size() == 0) {
                    System.out.println(Thread.currentThread().getName()+List);
                    break;
                } else {
                    Collections.shuffle(list);//打乱list数据
                    int prize = list.remove(0);
                    List.add(prize);
                }
            }
                Thread.sleep(100);//目的是可以让两个线程交替进行显示
        }
        //把集合中的最大值返回
        if(List.size()==0){
            return null;
        }else{
           return Collections.max(List);
        }

    }
}
public class test1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ArrayList<Integer> list=new ArrayList<>();
        Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
        MyCallable mc=new MyCallable(list);
        FutureTask<Integer> ft1=new FutureTask<>(mc);
        FutureTask<Integer> ft2=new FutureTask<>(mc);
        Thread t1=new Thread(ft1);
        Thread t2=new Thread(ft2);
        t1.setName("抽奖箱1");
        t2.setName("抽奖箱2");
        t1.start();
        t2.start();
        int max1=ft1.get();
        int max2=ft2.get();
        System.out.println(max1);
        System.out.println(max2);
    }
    }

线程池

核心原理:

创建线程池

创建一个没有上限的线程池:

public class MyRunnable implements Runnable{

    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            System.out.println(Thread.currentThread().getName()+"--"+i);
        }
    }
}
public class test1 {
    public static void main(String[] args)  {
        //创建线程池对象
      ExecutorService poll= Executors.newCachedThreadPool();
      //提交任务
        poll.submit(new MyRunnable());
        poll.submit(new MyRunnable());
        poll.submit(new MyRunnable());
        //线程池一般不会关闭
    }
    }

 

创建一个有上限的线程池:

public class test1 {
    public static void main(String[] args)  {
        //创建线程池对象
      ExecutorService poll= Executors.newFixedThreadPool(2);
      //提交任务
        poll.submit(new MyRunnable());
        poll.submit(new MyRunnable());
        poll.submit(new MyRunnable());
        //线程池一般不会关闭
    }
    }

自定义线程池

系统先会创建服务于任务1、2、3的线程1、2、3;再将任务4、5、6拿去排队; 再调用临时线程服务于任务7、8、9;任务10会触发任务拒绝策略。

任务拒绝策略

创建自定义线程池:

public class test1 {
    public static void main(String[] args)  {
        //自定义线程池
        ThreadPoolExecutor pool=new ThreadPoolExecutor(
                3,//核心线程的数量
                6,//最大线程数
                60,//空闲线程最大存活时间
                TimeUnit.SECONDS,//时间单位
                new ArrayBlockingQueue<>(3),//任务队列,相当于阻塞队列
                Executors.defaultThreadFactory(),//创建线程工厂:线程池如何获取到一个线程
                new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略
        );
    }
    }

线程池多大合适

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

03-240605-Spark笔记

03-240605 1. 行动算子-1 reduce 聚合 格式: def reduce(f: (T, T) > T): T 例子&#xff1a; val sparkConf new SparkConf().setMaster("local[*]").setAppName("Operator")val sc new SparkContext(sparkConf) ​val rdd sc.makeRDD(List(1…

MySQL第二种实现方式:现在有一个生产计划,甲乙丙3个品类共16个产品,生产时间6天,每天甲品类可以生产1张单,乙3张,丙1张,请用MySQL写出H列的效果

接上篇&#xff1a;链接: 现在有一个生产计划&#xff0c;甲乙丙3个品类共16个产品&#xff0c;生产时间6天&#xff0c;每天甲品类可以生产1张单&#xff0c;乙3张&#xff0c;丙1张&#xff0c;请用MySQL写出H列的效果 第二种写法&#xff1a; -- 使用WITH子句创建CTE WITH…

【Python报错】已解决ImportError: cannot import name ‘mapping‘ from ‘collections‘

成功解决“ImportError: cannot import name ‘mapping’ from ‘collections’”错误的全面指南 成功解决“ImportError: cannot import name ‘mapping’ from ‘collections’”错误的全面指南 一、引言 在Python编程中&#xff0c;当我们尝试从某个模块中导入某个名称时&…

QT学习过程中遇到的问题自记

文章目录 前言问题1问题2问题3 前言 学习QT嵌入式实战开发(从串口通信到JSON通信微课视频版)的过程中遇到的几个小问题 问题1 1.将书中的示例代码导入自己的电脑&#xff0c;然后点击工程进去&#xff0c;不能运行&#xff0c;报错 no kits are enabled for this project… 我…

python pip 安装

如果您不确定pip的安装路径&#xff0c;可以通过以下命令来查询&#xff1a; pip show pip 这个命令会显示pip的详细信息&#xff0c;其中包括pip安装的路径。如果您想修改pip的默认安装路径&#xff0c;可以使用pip的"--target"参数指定目标路径&#xff0c;例如&a…

解决跨域的几种方法

解决跨域的方法主要有以下几种&#xff1a; 1.CORS&#xff08;跨域资源共享&#xff09; CORS是一种W3C规范&#xff0c;它定义了一种浏览器和服务器交互的方式来确定是否允许跨源请求。 服务器通过设置响应头Access-Control-Allow-Origin来允许或拒绝跨域请求。例如&#xf…

tsconfig.json和tsconfig.app.json文件解析(vue3+ts+vite)

tsconfig.json {"files": [],"references": [{"path": "./tsconfig.node.json"},{"path": "./tsconfig.app.json"}] }https://www.typescriptlang.org/tsconfig/#files files: 在这个例子中&#xff0c;files 数…

Java数据结构- Map和Set

目录 1. Map和Set2. Map的使用3. Set的使用 1. Map和Set Java中&#xff0c;Map和Set是两个接口&#xff0c;TreeSet、HashSet这两个类实现了Set接口&#xff0c;TreeMap、HashMap这两个类实现了Map接口。 带Tree的这两个类&#xff08;TreeSet、TreeMap&#xff09;底层的数…

uniapp封装picker选择器组件,支持关键字查询

CommonPicker.vue组件 路径在 components\CommonPicker.vue <template><view><uni-easyinput v-model"searchQuery" :placeholder"placeholder" /><picker :range"filteredOptions" :range-key"text" v-model&…

六位一线AI工程师总结Agent构建经验,天工SkyAgents的Agent构建实战。

原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; 六位一线AI工程师总结Agent构建经验&#xff0c;天工SkyAgents的Agent构建实战。 &#x1f31f;我们给人类新手明确的目标和具体的计划&am…

浪潮电脑文件消失怎么恢复?原来有这五种方法

无论是工作、学习还是娱乐&#xff0c;电脑都扮演着举足轻重的角色。然而&#xff0c;在使用电脑的过程中&#xff0c;我们有时会遇到一些令人头疼的问题&#xff0c;比如文件突然消失。对于使用浪潮电脑的用户来说&#xff0c;文件消失可能是一个令人焦虑的问题。本文将为您详…

用负载绿原酸的纳米复合水凝胶调节巨噬细胞表型以加速伤口愈合

引用信息 文 章&#xff1a;Modulating macrophage phenotype for accelerated wound healing with chlorogenic acid-loaded nanocomposite hydrogel. 期 刊&#xff1a;Journal of Controlled Release&#xff08;影响因子&#xff1a;10.8&#xff09; 发表时间&a…

ubuntu系统(香蕉派)设置开机自启动

基本使用参考我之前发的&#xff1a;OrangePi AIpro从上手到网站部署使用-CSDN博客 以下介绍两种设置开机自启动的方法&#xff0c;分别对应界面中配置和在命令中配置 方法1&#xff1a; 编辑开启自启动命令 sudo nano /etc/rc.local # 在该文件中添加启动执行命令&#xff…

外企跨国大数据迁移的注意事项

跨国数据迁移&#xff0c;对汽车行业来说&#xff0c;是一桩大事。跨国公司在进行这一操作时&#xff0c;会遇到不少挑战&#xff0c;比如网络延迟、数据安全、成本控制等等。今天&#xff0c;咱们就聊聊跨国大数据迁移中&#xff0c;跨国车企需要留意的几个关键点。 跨国大数据…

主流的单片机语言是 C 吗?是的话为啥不是 C++?

是c&#xff0c;而且可以预见在很长很长一段时间&#xff0c;没有巨大变革的情况下都会是c 商业项目开发光讨论语言特性优劣问题&#xff0c;是非常片面的&#xff0c;所以要看待为什么是c&#xff0c;最主要仍然是从收益和成本上来看。 刚好我有一些资料&#xff0c;是我根据…

通过 SFP 接口实现千兆光纤以太网通信4

Tri Mode Ethernet MAC 与 1G/2.5G Ethernet PCS/PMA or SGMII 的连接 在设计中&#xff0c;需要将 Tri Mode Ethernet MAC 与 1G/2.5G Ethernet PCS/PMA or SGMII 之间通过 GMII 接口互联。Tri Mode Ethernet MAC IP 核的工作时钟源为 1G/2.5G Ethernet PCS/PMA or SGMII …

心理咨询系统|心理咨询系统成品开发功能

心理咨询系统开发后端设计是一个复杂且精细的过程&#xff0c;涉及多个关键领域的专业知识和技术。本文将详细探讨心理咨询系统开发后端设计的各个方面&#xff0c;包括系统架构、数据库设计、接口开发、安全性保障以及性能优化等。 首先&#xff0c;我们来谈谈系统架构。在心理…

指针还是学不会?跟着小代老师学,进入深入理解指针(4)

指针还是学不会&#xff1f;跟着小代老师学&#xff0c;进入深入理解指针&#xff08;4&#xff09; 1回调函数2qsort使用举例2.1使用qsort函数排序整行数据2.2使用qsort排序结构体数据 3qsort函数的模拟实现 1回调函数 回调函数就是一个通过函数指针调用的函数。 如果你把函数…

【窗口函数的详细使用】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;我是书生♡&#xff0c;今天主要和大家分享一下可MySQL中的窗口函数的概念&#xff0c;语法以及常用的窗口函数,希望对大家有所帮助。感谢大家关注点赞。 &#x1f49e;&#x1f49e;前路漫漫&#xff0c;希望大…

【TS】进阶

一、类型别名 类型别名用来给一个类型起个新名字。 type s string; let str: s "123";type NameResolver () > string;: // 定义了一个类型别名NameResolver&#xff0c;它是一个函数类型。这个函数没有参数&#xff0c;返回值类型为string。这意味着任何被…