JAVA线程

目录

一、可重入锁

二、死锁

三、内存可见性问题

四、wait和notify

五、单例模式

六、生产者消费者模型

七、定时器

 八、线程池

一、可重入锁

public class test {
    public static void main(String[] args) {
        Object locker=new Object();
        Thread t1=new Thread(()->{
            synchronized(locker){
                System.out.println("线程t的第一层锁");
                synchronized(locker){
                    System.out.println("线程t的第二层锁");
                }
            }
        });
        t1.start();
    }
}

这段代码大家认为结果是什么???“线程t的第二层锁”会被打印吗???

对于这种同一个线程加多次锁,线程不会处于阻塞状态,对应的操作是遇到“{”锁计数器+1,遇到“}”锁计数器-1,当计数器为0时锁释放。

二、死锁

1、死锁场景

(1)一个线程一把锁

对于上述的代码来说,如果锁不是可重入锁,则一个线程重复加锁两次就会出现死锁问题。

(2)两个线程两把锁

线程1获取锁a,线程2获取锁b,在以上锁都未释放时,线程1尝试获取锁b,线程2尝试获取锁a,两个线程会处于阻塞状态,出现死锁。

public class test1 {
    public static void main(String[] args) {
        Object a=new Object();
        Object b=new Object();
        Thread t1=new Thread(()->{
            synchronized(a){
                System.out.println("线程t1获取到a锁");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized(b){
                    System.out.println("线程t2获取到b锁");
                }
            }
        });
        Thread t2=new Thread(()->{
            synchronized(b){
                System.out.println("线程t2获取到b锁");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized(a){
                    System.out.println("线程t2获取到a锁");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

看两个线程的状态为:BLOCKED,表明线程处于阻塞状态。

(3)N个线程M把锁

2、产生死锁的4个必要条件

(1)锁是互斥使用的,一个线程获取了锁A,另一个线程也想获取锁A就必须阻塞等待。

(2)锁是不可抢占的,一个线程获取了锁A,只有等线程主动释放了锁A,下一个线程才能获取锁A。

(3)请求保持,一个线程在拿到锁A后,在持有锁A的状态下尝试获取锁B。

(4)循环等待。

3、解决死锁

(1)增加锁的数量

(2)减少线程

(3)限制同一时间线程的运行个数

(4)规定加锁顺序

三、内存可见性问题

1、问题

import java.util.Scanner;

public class test2 {
    private static int flag=0;
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while (flag==0){

            }
            System.out.println("线程t1结束");
        });
        t1.start();
        Scanner sc=new Scanner(System.in); 
        flag=sc.nextInt();
    }
}

上述代码若输入非0的数,代码就会打印“线程t1结束”并结束进程???

可以看到线程t1的状态仍为RUNNABLE,还未结束进程,为什么输入1未使线程t1结束???

以上就是内存可见性问题,对于线程t1中的指令flag==0,先从内存中取出flag的值并放入cpu的寄存器里,再拿出寄存器中的flag与0进行比较。由于上述循环执行的非常快,而指令中的flag值短时间内没有变化,访问内存的速度要比访问寄存器的速度慢许多,于是JVM就进行了优化,不再重复读取内存中flag的值,直接使用cpu寄存器中缓存的flag值,导致后续flag的值发生了改变但是循环不再使用内存中flag的值。

2、解决方法

在flag变量中加入volatile关键字,保证内存可见性,cpu必须每次访问内存中该变量。

import java.util.Scanner;
public class test2 {
    private volatile static int flag=0;
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while (flag==0){

            }
            System.out.println("线程t1结束");
        });
        t1.start();
        Scanner sc=new Scanner(System.in);
        flag=sc.nextInt();
    }
}

3、JMM(Java Memory Model )

上面的现象为:在上面的代码中,编译器发现,每次循环都要读取内存,开销太大,于是就把读取内存的操作优化为读取寄存器操作,提高代码运行效率。

JMM模型表述:在上面代码中,编译器发现,每次循环都要读取“主内存”,开销太大,于是就把“主内存”的值复制到“工作内存”(寄存器+缓存)中,后续循环直接读取“工作内存”的值。

四、wait和notify

1、wait作用

当某个线程获取到锁后,发现当前还不满足执行的条件,就可以调用对象锁的wait方法,主动放弃被调度的机会,进入等待状态。

2、notify作用

第一个线程不满足条件后通过wait方法进入阻塞状态,其他线程在运行时使条件满足,此时就可以调用notify方法唤醒第一个线程。

3、wait和notify使用例子 

有三个线程:t1,t2,t3,每个线程打印自己的名称,同时启动,并按t3,t2,t1的顺序打印

从题目获取到信息:先运行线程t3,当t3打印完时再运行t2,当t2打印完时再运行t1。即:线程t2与线程t1刚开始需要wait,当线程t3打印完后notify线程t2,当线程t2打印完后notify线程t1。

需注意:wait是要释放锁,释放的前提是要有锁,即:wait方法需在锁中调用,锁对象.wait();notify方法也需在锁中执行(java规定),需要唤醒哪个线程,就使用该线程的锁对象,锁对象.notify()

public class test3 {
    public static void main(String[] args) {
        Object lock1=new Object();
        Object lock2=new Object();
        Thread t1=new Thread(()->{
            synchronized(lock1){
                try {
                    lock1.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            System.out.println("线程t1");
        });
        Thread t2=new Thread(()->{
            synchronized(lock2){
                try {
                    lock2.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程t2");
            synchronized (lock1){
                lock1.notify();
            }
        });
        Thread t3=new Thread(()->{
            System.out.println("线程t3");
            synchronized (lock2){
                lock2.notify();
            }
        });
        t1.start();
        t2.start();
        t3.start();
    }
}

4、wait和notify运行机制

当线程调用wait方法后,内部干了三件事:释放锁;线程进入阻塞状态;等待其他线程调用notify,其他线程调用notify后,线程重新拿到锁(可能也需要和其他线程竞争)。正常情况下:先执行wait,在执行notify

5、wait和notify的方法

不带时间的wait(),若没有其他线程调用notify()就死等;

带时间的wait(),若没有其他线程调用notify()且时间超过给定时间,线程就不再等待,继续运行。

notify()方法:若有多个线程被阻塞等待,且锁对象相同,调用notify()后就只能随机唤醒其中一个;

notifyAll()方法:若有多个线程被阻塞等待,且锁对象相同,调用notifyAll()后,多个线程被同时唤醒,此时会存在锁竞争。

6、wait()、join()、sleep()区别

wait():是object类的方法;没有时间的wait就只能通过其他线程调用notify方法被唤醒;有时间的wait在达到给定时间后未被唤醒,线程就不再阻塞;wait()必须在锁中使用。

join():是Thread类的方法;没有时间的join就只能在被等待的线程运行完成后才会被唤醒;有时间的join在达到给定时间后未被唤醒,线程就不再阻塞

sleep():是Thread类的方法;sleep()操作不需要锁,在达到给定时间后就会被唤醒,若想提前唤醒就只能通过interrupt唤醒(异常唤醒)。

五、单例模式

1、概念

单例模式是一种设计模式,单例指某个类,在一个进程中,只允许创建一个实例。

2、饿汉模式

package single;
class singleDon{
    private static singleDon example=new singleDon();
    private singleDon(){

    }

    public static singleDon getExample() {
        return example;
    }
}
public class test {
    public static void main(String[] args) {
        singleDon example=singleDon.getExample();
    }
}

每个类的类对象只会存在一个,即:在上述代码中只会存在一个example实例。且该实例在类加载是就已创建好。构造方法为private,使得该类无法创造出其他实例,只能调用getExample()方法获取实例。

3、懒汉模式

package single;

class singleTonLazy{
    private static singleTonLazy example;
    public static singleTonLazy getExample(){
        if (example==null){
            example=new singleTonLazy();
        }
        return example;
    }
    private singleTonLazy(){

    }
}
public class test1 {
    public static void main(String[] args) {
        singleTonLazy example1 =singleTonLazy.getExample();
    }
}

与饿汉模式的不同点在于:懒汉模式的实例并没有随着类的加载而创建好,而是需要是再调用getExample()方法创建实例(实例为空时)。

4、有关单例模式的线程不安全问题

上述两种模式有无线程不安全问题???

饿汉模式没有线程不安全问题。饿汉模式相当于只进行了读,读不会引起线程安全问题,每次读到的都是实例。

懒汉模式有线程不安全问题。试想一下:线程A运行到if后的判空语句,还未进行创建实例,此时cpu开始调度线程B,由于线程A未创建对象,线程B判断为空创建了对象,线程B执行完成后接着执行A又创建了对象,导致该类创建2个实例,不是单例模式。

5、懒汉模式改进

对于上述线程不安全问题,可以将if和new两个操作打包成一个原子(加锁),问题来了锁加在哪里合适呢???

若直接加在if上,那么对于后面已经创建了对象的,就只是一个读操作,读操作没有线程不安全问题,若还有锁运行效率是否有点低???所以应该进行if判断,若有对象直接返回,没有对象时再将if和new打包成一个原子创建对象。

package single;

class singleTonLazy1{
    private static singleTonLazy1 example;
    static Object lock = new Object();
    public static singleTonLazy1 getExample(){
        if(example==null){  //第一个if判断是否要加锁
            synchronized(lock){
                if (example==null){  //第二个if判断是否要创建对象
                    example=new singleTonLazy1();
                }
            }
        }
        return example;
    }
    private singleTonLazy1(){

    }
}
public class test2 {
    public static void main(String[] args) {
        singleTonLazy example1 =singleTonLazy.getExample();
    }
}

6、指令重排序

对于上述的代码还存在指令重排序问题。

example = new singleTonLazy1() 这一指令中有三个步骤:①申请一段内存空间;②在内存上调用构造方法创造出实例;③将内存地址赋值给example1变量。这一指令可以按照①②③运行,也可以按照①③②运行,对于单线程来说,指令重排序不会造成什么影响,最后都是实例化example1对象,但若对多线程就不一定了。假如线程A对于这一指令按照①③②的步骤运行,运行到③步骤时,example1不为空,但尚未初始化,此时又开始调度线程B,example1不为空,但若要使用example1的属性时,此时还未初始化就会出现问题,这就是指令重排序问题。

如何解决指令重排序问题???在上面谈论内存可见性问题时我们提到:给变量加入volatile关键字,可以保证内存可见性,cpu必须每次访问内存中该变量。同样,我们若给变量加入volatile关键字,也可以杜绝指令重排序,可以按照原本的①②③步骤执行,就不会出现变量未初始化问题。

六、生产者消费者模型

1、阻塞队列

阻塞队列是基于普通队列做出的扩展,相对于普通队列来说:①阻塞队列是线程安全的;②阻塞队列具有阻塞功能。对一个已经满了的队列进行入队列时,入队列操作会被阻塞,一直阻塞到有元素出队列(队列不满时);对一个已经空了的队列进行出队列时,出队列操作会被阻塞,一直阻塞到有元素入队列(队列不空时)。

2、基于阻塞队列下的生产者消费者模型

(1)解耦合

上图为“分布式分布系统”,服务器整个的功能不是由一个服务器全部完成的,而是每个服务器负责一部分功能,通过服务器之间的网络通信,最终完成整个功能。如:客户端想获取到主页信息,则将该请求发给A入口服务器,A将请求分别发给用户服务器和商品服务器,获取到用户信息和商品信息后,再将它通过A返回到客户端,形成完整的主页信息。该模型中,A和B以及A和C之间耦合性比较强,如果B或者C挂了,则A服务器也可能挂了。

上图为生产者消费者模型,引入了阻塞队列(消息队列),A服务器将请求发送给阻塞队列,用户服务器和商品服务器直接去阻塞队列中获取请求,降低了A和B以及A和C之间的耦合性。

(2)削峰填谷

在上述模型中,若同一时间,客户端的请求突然变多,此时很可能会导致服务器B和服务器C挂了

上图为生产者消费者模型,引入了阻塞队列(消息队列),A服务器将请求发送给阻塞队列,用户服务器和商品服务器直接去阻塞队列中获取请求,B服务器和C服务器获取消息的频率是稳定的,即使客户端请求变多,B和C也只是从阻塞队列中稳定的获取请求。

3、Java中有关阻塞队列的标准库

BlockingQueue是一个接口,该接口下有:链表阻塞队列、数组阻塞队列、优先级阻塞队列。

阻塞队列拥有的方法,put是带有阻塞功能的入队列,take是带有阻塞功能的出队列。

4、阻塞队列底层实现

基于数组实现阻塞队列(环形队列)

class myArrayListQueue{
    private String[] elems=null;
    private int head=0;
    private int tail=0;
    private int size=0;
    public myArrayListQueue(int n){
        elems=new String[n];
    }
    Object lock=new Object();
    public void put(String elem) throws InterruptedException {
        synchronized (lock){
            if(size>=elems.length){
                lock.wait();  //满了被阻塞
            }
            elems[tail]=elem;
            tail++;
            if(tail>=elems.length){
                tail=0;
            }
            size++;
            lock.notify();  //唤醒take阻塞
        }
    }
    public String take() throws InterruptedException {
        String elem=null;
        synchronized (lock){
            if (size==0){
                lock.wait(); //空了被阻塞
            }
           elem=elems[head];
            head++;
            if(head>=elems.length){
                head=0;
            }
            size--;
            lock.notify(); //唤醒put被阻塞
        }
        return elem;
    }
}

以上代码写的对不对???试想一下:put中的notify就只能唤醒被阻塞的take吗???take中的notify就只能唤醒被阻塞的put吗???假如此时有三个线程:A、B、C,线程A因为空被阻塞,线程B因为空被阻塞,线程C进入take方法,放入元素后唤醒了A(也可能是B),此时A出元素,并唤醒线程,而被唤醒的线程也可能是B(锁对象一样),但此时队列是空的,出元素异常。所以我们希望在B被唤醒后在进行判断,如列表为空依旧被阻塞,列表不为空则放入元素。考虑while

class myArrayListQueue{
    private String[] elems=null;
    private int head=0;
    private int tail=0;
    private int size=0;
    public myArrayListQueue(int capacity){
        elems=new String[capacity];
    }
    private Object lock=new Object();
    public void put(String elem) throws InterruptedException {
        synchronized (lock){
            while(size>=elems.length){
                lock.wait();  //满了被阻塞
            }
            elems[tail]=elem;
            tail++;
            if(tail>=elems.length){
                tail=0;
            }
            size++;
            lock.notify();  //唤醒take阻塞
        }
    }
    public String take() throws InterruptedException {
        String elem1=null;
        synchronized (lock){
            while(size==0){
                lock.wait(); //空了被阻塞
            }
           elem1=elems[head];
            head++;
            if(head>=elems.length){
                head=0;
            }
            size--;
            lock.notify(); //唤醒put被阻塞
        }
        return elem1;
    }
}
public class test {
    public static void main(String[] args) {
        myArrayListQueue queue=new myArrayListQueue(1000);
        Thread t=new Thread(()->{  //生产者
            int n=1;
            while (true){
                try {
                    queue.put(n+" ");
                    System.out.println("生产元素:"+n);
                    n++;
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t1=new Thread(()->{  //消费者
            while (true){
                try {
                    String n=queue.take();
                    System.out.println("消费者元素:"+n);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        t1.start();
    }
}

七、定时器

1、概念

设置一个等待时间,当时间到了之后就执行某个线程。

2、java中提供的定时器实现

import java.util.Timer;
import java.util.TimerTask;
public class test1 {
    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("线程 3000");
            }
        },3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("线程 2000");
            }
        },2000);
        System.out.println("主线程");
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("线程 1000");
            }
        },1000);
    }
}

Timer是实现定时器的工具,通过schedule方法向定时器中加入定时任务(TimerTask)和定时时间(delay),这个时间是相对时间间隔,以当前时间为基准,计算delay时间后运行。定时器内部有一个线程,在任务时间到了之后线程就会运行相应代码。在所有任务运行完成后,进程也没有结束,因为Timer中的线程为前台线程,运行完后就处于“严阵以待”状态,等待下一个任务安排。若想结束前台线程,可以调用cancel方法结束。

3、定时器底层实现

结合上述,我们需要一个线程t去运行任务;需要一个优先级队列,按等待的时间放入队列。需要一个schedule方法,向队列中放入任务并唤醒因为空等待的线程;线程t扫描队列运行任务,队列为空进入等待;任务等待时间还未到,进入等待,但不能用sleep等待,因为该过程需要释放锁以防队列加入等待时间更短的任务,任务等待时间已到执行任务,并从队列中删除任务。

import java.util.PriorityQueue;
//定义任务
class myTimerTask implements Comparable<myTimerTask>{
    private long time;
    private Thread thread;
    public myTimerTask(Thread thread,long delay){
        this.thread=thread;
        this.time=System.currentTimeMillis()+delay;
    }

    @Override
    public int compareTo(myTimerTask o) {
        return (int)(this.time-o.time);
    }

    public long getTime() {
        return time;
    }
    public void run(){
        this.thread.run();
    }
}
//定义线程t和队列
class myTimer{
    PriorityQueue<myTimerTask> queue=new PriorityQueue<>();
    Thread t;
    Object lock=new Object();
    public void schedule(Thread thread,long delay){
        myTimerTask task=new myTimerTask(thread,delay);
        synchronized(lock){
            queue.offer(task);
            lock.notify();
        }
    }
    public myTimer(){
        t=new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                synchronized (lock){
                    if(queue.isEmpty()){
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            break;
                        }
                    }
                    myTimerTask task1=queue.peek();
                    long curtime=System.currentTimeMillis();
                    if(curtime>=task1.getTime()){
                        task1.run();
                        queue.poll();
                    }else {
                        try {
                            lock.wait(task1.getTime()-curtime);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
        t.start();
    }
    public void cancel() throws InterruptedException {
        Thread.sleep(8000);
        System.out.println("定时器结束工作");
        t.interrupt();
    }
}
public class test {
    public static void main(String[] args) throws InterruptedException {
        myTimer timer=new myTimer();
        timer.schedule(new Thread() {
            @Override
            public void run() {
                System.out.println("线程3000");
            }
        },3000);
        timer.schedule(new Thread(){
            @Override
            public void run() {
                System.out.println("线程2000");
            }
        },2000);
        timer.schedule(new Thread() {
            @Override
            public void run() {
                System.out.println("线程1000");
            }
        },1000);
        System.out.println("主线程");
        timer.cancel();
    }
}

 八、线程池

1、概念

把要使用的线程提前创建好,用完了也不直接释放,而是放入池中,下次使用的时候直接在池中拿即可。这样做节省了线程创建/释放的资源开销。

2、为什么从线程池里取线程比系统申请更高效???

因为从线程池取线程是纯粹的用户态(可控)操作,从系统创建线程涉及到内核态(不可控)和用户态的切换

 3、java标准库中的线程池

java的线程池和ThreadPoolExector类有关,该类最多可以有7个参数。

(1)int corePoolSize:核心线程数,指在一个线程池里最少得有多少个线程;

(2)int maximumPoolSize:最大线程数,指在一个线程池里最多能有多少个线程;

(3)long keepAliveTime:除核心线程以外,允许其他线程空闲时间,若其他线程超过时间阈值,则该线程就会被销毁;

(4)TimeUnit unit:时间单位(ms,s,min,hour....);

(5)BlockingQueue<Runnable> workQueue:阻塞队列,该队列中放任务,也可以使用优先级队列;

(6)ThreadFactory threadFactory:线程工厂类,创建线程对象。

工厂模式:解决构造方法的不足。

例如:用point类表示坐标上的一个点:

class point{
   public x;
   public y;
   public point(int x,int y){   //直角坐标系下
        this.x=x;
        this.y=y;
   }
   public point(int r,int a){   //极坐标系下
        this.x=r.cosa;
        this.y=r.sina;
   }
}

以上代码编译能通过吗???不能通过,因为构造方法之间没有构成重载。

为了解决上述问题,我们引入了工厂模式,使用普通的方法创建对象。

class Point{
    public static Point makePoint(int x,int y){
          Point p=new Point();
          p.setX(x);
          p.setY(y);
          return p;
    }
    public static Point makePoint1(int r,int a){
          Point p=new Point();
          p.setX(r*cosa);
          p.setY(r*sina);
          return p;
    }
}

public class test{
   public static void main(String[] args){
       Point p=Point.makePoint(x,y);
       Point p1=Point.makePoint1(r,a);
   }
}

通过静态方法封装new操作,在方法内部设置不同的属性完成对象初始化,构造对象的过程就是工厂模式。

(7)RejectedExecutionHandler handler:拒绝策略,即当任务队列已经满了,此时若继续往队列中添加任务,线程池会怎么做???

①第一种策略:抛出异常,结束运行;

②第二种策略:新添加的任务由添加任务的线程执行,不由线程池中的线程执行;

③第三种策略:线程放弃执行最早的任务,执行最新的任务;

④第四种策略:新添加的任务不执行,线程池中的线程不执行新任务,添加任务的线程也不执行。

4、Executors 工厂类

ThreadPoolExector类用起来太复杂,因此标准库还提供了另一个和线程池有关的类:Executors。

工厂类,通过这个类来创建不同的线程池对象。

(1)newSingleThreadExecutor() :指线程池里只有单个的线程;

(2)newScheduledThreadPool(int corePoolSize):类似于定时器,可以延时执行任务;

(3)newCachedThreadPool():线程数目可以动态扩容;

(4)newFixedThreadPool(int nThreads):线程数目是固定的;

通过以上方法创建一个线程池对象(ExecutorService),并通过submit方法向线程池中添加任务。

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

public class test1 {
    public static void main(String[] args) {
        ExecutorService service= Executors.newFixedThreadPool(4);
        service.submit(new Thread(){
            @Override
            public void run(){
                System.out.println("线程1");
            }
        });
        System.out.println("主线程");
    }
}

5、创建线程池的时候,很多时候都需要设定线程的个数,那么这个数目设定为多少合适???

对于这个问题必须具体问题具体分析,要关注一个线程是cpu密集型任务,还是IO密集型任务。例如:一个进程中,如果所有的线程都是cpu密集型任务,每个线程都是在cpu上运行的,此时线程数目就不应该超过N(cpu逻辑核心数);若一个进程中,如果所有的线程都是IO密集型任务,cpu消耗非常少,此时线程数目就可以远远超过N。上述两种是极端情况,对于实际情况来说,cpu密集型任务和IO密集型任务线程数目的比例不清楚,可以通过不断实验确定最合适的线程数目。

6、底层实现线程池

(1)需要有一个阻塞队列,该队列中装要执行的任务;

(2)需要有一个构造方法,参数为线程池中线程的个数,创建线程,并不断扫描队列执行任务;

(3)需要有一个submit方法,往队列中放任务

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class myExectorPool{
    BlockingQueue<Thread> queue=new ArrayBlockingQueue<>(1000);
    public myExectorPool(int n){
        for (int i=0;i<n;i++){
            Thread t=new Thread(()->{
                while(true){
                    try {
                        Thread t1=queue.take();
                        t1.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }
    public void submit(Thread t) throws InterruptedException {
        queue.put(t);
    }
}
public class test2 {
    public static void main(String[] args) throws InterruptedException {
        myExectorPool myexectorpool=new myExectorPool(4);
        for (int i=0;i<1000;i++){
            int n=i;
            myexectorpool.submit(new Thread(){
                @Override
                public void run() {
                    System.out.println("执行任务"+n+"  执行的线程名"+Thread.currentThread().getName());
                }
            });
        }
    }
}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ambition…

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值