多线程

news2024/9/22 15:37:02

标题

  • 创建多线程方式一:继承
    • 线程中常用方法和优先级
    • 多窗口卖票
  • 创建多线程方式二:实现Runnable接口
    • 多窗口卖票(使用Runable方式)
  • 进程的生命周期
  • 同步代码块解决实现Runable的线程安全问题
    • 方法一
    • 方法二
  • 使用同步方法处理实现Runable的线程安全问题
  • 使用同步方法处理继承Thread类的方式中的线程安全问题
  • 死锁问题
  • Lock锁方式解决线程安全问题
  • 练习
  • 线程的通信
  • 创建多线程的方式三:实现Callable接口
  • 创建多线程的方式四:使用线程池

创建多线程方式一:继承

 * 多线程的创建,方式一:继承于Thread* 1. 创建一个继承于Thread类的子类
 * 2. 重写Thread类的run() --> 将此线程执行的操作声明在run()* 3. 创建Thread类的子类的对象
 * 4. 通过此对象调用start()
 * <p>
 * 例子:遍历100以内的所有的偶数

//1. 创建一个继承于Thread类的子类
class MyThread extends Thread {
    //2. 重写Thread类的run()
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}


public class ThreadTest {
    public static void main(String[] args) {
        //3. 创建Thread类的子类的对象
        MyThread t1 = new MyThread();

        //4.通过此对象调用start():①启动当前线程 ② 调用当前线程的run()
        t1.start();
        //问题一:我们不能通过直接调用run()的方式启动线程。-->不可以
//        t1.run();

        //问题二:再启动一个线程,遍历100以内的偶数。不可以还让已经start()的线程去执行。会报IllegalThreadStateException
//        t1.start();
        //我们需要重新创建一个线程的对象
        MyThread t2 = new MyThread();
        t2.start();


        //如下操作仍然是在main线程中执行的。
        for (int i = 0; i < 50; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i + "***********main()************");
            }
        }
    }
}

输出:

main:0***********main()************
Thread-1:0
Thread-0:0
Thread-1:2
Thread-1:4
main:2***********main()************
Thread-1:6
Thread-0:2
Thread-1:8
main:4***********main()************
Thread-1:10
Thread-0:4
Thread-1:12
main:6***********main()************
Thread-1:14
Thread-1:16
Thread-1:18
Thread-1:20
Thread-0:6
Thread-0:8
Thread-1:22
main:8***********main()************
main:10***********main()************
main:12***********main()************
main:14***********main()************
main:16***********main()************
Thread-1:24
Thread-0:10
Thread-0:12
Thread-0:14
Thread-0:16
Thread-1:26
main:18***********main()************
Thread-1:28
Thread-1:30
Thread-1:32
Thread-1:34
Thread-1:36
Thread-1:38
Thread-1:40
Thread-1:42
Thread-1:44
Thread-1:46
Thread-1:48
Thread-0:18
main:20***********main()************
main:22***********main()************
main:24***********main()************
main:26***********main()************
main:28***********main()************
main:30***********main()************
main:32***********main()************
main:34***********main()************
main:36***********main()************
main:38***********main()************
main:40***********main()************
main:42***********main()************
Thread-0:20
main:44***********main()************
main:46***********main()************
Thread-0:22
main:48***********main()************
Thread-0:24
Thread-0:26
Thread-0:28
Thread-0:30
Thread-0:32
Thread-0:34
Thread-0:36
Thread-0:38
Thread-0:40
Thread-0:42
Thread-0:44
Thread-0:46
Thread-0:48

Process finished with exit code 0

线程中常用方法和优先级

 * 测试Thread中的常用方法:
 * 1. start():启动当前线程;调用当前线程的run()
 * 2. run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
 * 3. currentThread():静态方法,返回执行当前代码的线程
 * 4. getName():获取当前线程的名字
 * 5. setName():设置当前线程的名字
 * 6. yield():释放当前cpu的执行权
 * 7. join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才
 *           结束阻塞状态。
 * 8. stop():已过时。当执行此方法时,强制结束当前线程。
 * 9. sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前
 *                          线程是阻塞状态。
 * 10. isAlive():判断当前线程是否存活
 *
 *
 * 线程的优先级:
 * 1.
 * MAX_PRIORITY10
 * MIN _PRIORITY:1
 * NORM_PRIORITY5  -->默认优先级
 * 2.如何获取和设置当前线程的优先级:
 *   getPriority():获取线程的优先级
 *   setPriority(int p):设置线程的优先级
 *
 *   说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下
 *   被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
 *

多窗口卖票

例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式(存在线程的安全问题,待解决。)

package com.pan.exer;

import org.junit.Test;

class Window extends Thread{
    private static int ticket = 30;

    @Override
    public void run(){
        while(true){
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
                ticket--;
            }
            else
                break;
        }
    }
}


public class WindowTest {
    @Test
    public void test1(){
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

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

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

输出:

窗口1:卖票,票号为:30
窗口3:卖票,票号为:30
窗口3:卖票,票号为:28
窗口3:卖票,票号为:27
窗口3:卖票,票号为:26
窗口3:卖票,票号为:25
窗口3:卖票,票号为:24
窗口2:卖票,票号为:30
窗口3:卖票,票号为:23
窗口3:卖票,票号为:21
窗口3:卖票,票号为:20
窗口3:卖票,票号为:19
窗口3:卖票,票号为:18
窗口3:卖票,票号为:17
窗口3:卖票,票号为:16
窗口3:卖票,票号为:15
窗口3:卖票,票号为:14
窗口3:卖票,票号为:13
窗口3:卖票,票号为:12
窗口3:卖票,票号为:11
窗口3:卖票,票号为:10
窗口3:卖票,票号为:9
窗口3:卖票,票号为:8
窗口3:卖票,票号为:7
窗口3:卖票,票号为:6
窗口3:卖票,票号为:5
窗口3:卖票,票号为:4
窗口3:卖票,票号为:3
窗口3:卖票,票号为:2
窗口3:卖票,票号为:1
窗口1:卖票,票号为:29
窗口2:卖票,票号为:22

创建多线程方式二:实现Runnable接口

 * 创建多线程的方式二:实现Runnable接口
 * 1. 创建一个实现了Runnable接口的类
 * 2. 实现类去实现Runnable中的抽象方法:run()
 * 3. 创建实现类的对象
 * 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
 * 5. 通过Thread类的对象调用start()


* 比较创建线程的两种方式。
 * 开发中:优先选择:实现Runnable接口的方式
 * 原因:1. 实现的方式没有类的单继承性的局限性
 *       2. 实现的方式更适合来处理多个线程有共享数据的情况。
 * 联系:public class Thread implements Runnable
 * 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
//1. 创建一个实现了Runnable接口的类
class MThread implements Runnable{

    //2. 实现类去实现Runnable中的抽象方法:run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }

        }
    }
}


public class ThreadTest1 {
    public static void main(String[] args) {
        //3. 创建实现类的对象
        MThread mThread = new MThread();
        //4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t1 = new Thread(mThread);
        t1.setName("线程1");
        //5. 通过Thread类的对象调用start():① 启动线程 ②调用当前线程的run()-->调用了Runnable类型的target的run()
        t1.start();

        //再启动一个线程,遍历100以内的偶数
        Thread t2 = new Thread(mThread);
        t2.setName("线程2");
        t2.start();
    }

}

多窗口卖票(使用Runable方式)

/*
* 例子:创建三个窗口卖票,总票数为40张,使用实现Runable接口的方式
* */
class Window1 implements Runnable{
    private int ticket = 40;

    @Override
    public void run() {
        while(true){
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}
public class WindowTest1{
    @Test
    public void test1(){
        Window1 w = new Window1();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

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

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

输出:

窗口1:卖票,票号为:40
窗口3:卖票,票号为:40
窗口2:卖票,票号为:40
窗口3:卖票,票号为:38
窗口3:卖票,票号为:36
窗口3:卖票,票号为:35
窗口3:卖票,票号为:34
窗口3:卖票,票号为:33
窗口3:卖票,票号为:32
窗口3:卖票,票号为:31
窗口3:卖票,票号为:30
窗口3:卖票,票号为:29
窗口3:卖票,票号为:28
窗口3:卖票,票号为:27
窗口3:卖票,票号为:26
窗口3:卖票,票号为:25
窗口3:卖票,票号为:24
窗口3:卖票,票号为:23
窗口1:卖票,票号为:39
窗口1:卖票,票号为:21
窗口1:卖票,票号为:20
窗口1:卖票,票号为:19
窗口1:卖票,票号为:18
窗口1:卖票,票号为:17
窗口3:卖票,票号为:22
窗口3:卖票,票号为:15
窗口3:卖票,票号为:14
窗口2:卖票,票号为:37
窗口3:卖票,票号为:13
窗口3:卖票,票号为:11
窗口3:卖票,票号为:10
窗口3:卖票,票号为:9
窗口3:卖票,票号为:8
窗口3:卖票,票号为:7
窗口3:卖票,票号为:6
窗口3:卖票,票号为:5
窗口1:卖票,票号为:16
窗口3:卖票,票号为:4
窗口3:卖票,票号为:2
窗口3:卖票,票号为:1
窗口2:卖票,票号为:12
窗口1:卖票,票号为:3

进程的生命周期

在这里插入图片描述

同步代码块解决实现Runable的线程安全问题

 * 例子:创建三个窗口卖票,总票数为100.使用实现Runnable接口的方式
 *
 * 1.问题:卖票过程中,出现了重票、错票 -->出现了线程的安全问题
 * 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
 * 3.如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他
 *            线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。
 *
 *
 * 4.Java中,我们通过同步机制,来解决线程的安全问题。
 *
 *  方式一:同步代码块
 *
 *   synchronized(同步监视器){
 *      //需要被同步的代码
 *
 *   }
 *  说明:1.操作共享数据的代码,即为需要被同步的代码。  -->不能包含代码多了,也不能包含代码少了。
 *       2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
 *       3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
 *          要求:多个线程必须要共用同一把锁。
 *
 *       补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
 *  方式二:同步方法。
 *     如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。
 *
 *
 *  5.同步的方式,解决了线程的安全问题。---好处
 *    操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。 ---局限性
 *

方法一

class Window1 implements Runnable{
    private int ticket = 40;
    Object obj = new Object();


    @Override
    public void run() {
        while(true) {
            synchronized (this){//synchronized (obj) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}
public class WindowTest1{
    @Test
    public void test1(){
        Window1 w = new Window1();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

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

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

输出:

窗口1:卖票,票号为:40
窗口3:卖票,票号为:39
窗口3:卖票,票号为:38
窗口3:卖票,票号为:37
窗口3:卖票,票号为:36
窗口3:卖票,票号为:35
窗口3:卖票,票号为:34
窗口3:卖票,票号为:33
窗口3:卖票,票号为:32
窗口3:卖票,票号为:31
窗口3:卖票,票号为:30
窗口3:卖票,票号为:29
窗口3:卖票,票号为:28
窗口3:卖票,票号为:27
窗口3:卖票,票号为:26
窗口3:卖票,票号为:25
窗口3:卖票,票号为:24
窗口3:卖票,票号为:23
窗口3:卖票,票号为:22
窗口3:卖票,票号为:21
窗口3:卖票,票号为:20
窗口3:卖票,票号为:19
窗口3:卖票,票号为:18
窗口3:卖票,票号为:17
窗口3:卖票,票号为:16
窗口3:卖票,票号为:15
窗口3:卖票,票号为:14
窗口3:卖票,票号为:13
窗口3:卖票,票号为:12
窗口3:卖票,票号为:11
窗口3:卖票,票号为:10
窗口3:卖票,票号为:9
窗口3:卖票,票号为:8
窗口3:卖票,票号为:7
窗口3:卖票,票号为:6
窗口3:卖票,票号为:5
窗口3:卖票,票号为:4
窗口3:卖票,票号为:3
窗口3:卖票,票号为:2
窗口3:卖票,票号为:1

方法二


使用同步方法处理实现Runable的线程安全问题

package com.pan.test1;
/*
* 使用同步方法处理实现Runable的线程安全问题
* */
class Window2 implements Runnable{
    private int ticket = 200;

    @Override
    public void run(){
        while(true){
            show();
        }
    }

    private synchronized void show(){//同步监视器:this
        if(ticket > 0){
            try{
                Thread.sleep(300);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
            ticket--;
        }
    }
}

public class WindowTest2 {
    public static void main(String[] args) {
        Window2 w = new Window2();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

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

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

使用同步方法处理继承Thread类的方式中的线程安全问题

class Window4 extends Thread {


    private static int ticket = 300;

    @Override
    public void run() {

        while (true) {

            show();
        }

    }
    private static synchronized void show(){//同步监视器:Window4.class
        //private synchronized void show(){ //同步监视器:t1,t2,t3。此种解决方式是错误的
        if (ticket > 0) {

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
            ticket--;
        }
    }
}


public class WindowTest3 {
    public static void main(String[] args) {
        Window4 t1 = new Window4();
        Window4 t2 = new Window4();
        Window4 t3 = new Window4();

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

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

死锁问题

有死锁的代码

package com.atguigu.java1;
//死锁的演示
class A {
	public synchronized void foo(B b) { //同步监视器:A类的对象:a
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 进入了A实例的foo方法"); // ①
//		try {
//			Thread.sleep(200);
//		} catch (InterruptedException ex) {
//			ex.printStackTrace();
//		}
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 企图调用B实例的last方法"); // ③
		b.last();
	}

	public synchronized void last() {//同步监视器:A类的对象:a
		System.out.println("进入了A类的last方法内部");
	}
}

class B {
	public synchronized void bar(A a) {//同步监视器:b
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 进入了B实例的bar方法"); // ②
//		try {
//			Thread.sleep(200);
//		} catch (InterruptedException ex) {
//			ex.printStackTrace();
//		}
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 企图调用A实例的last方法"); // ④
		a.last();
	}

	public synchronized void last() {//同步监视器:b
		System.out.println("进入了B类的last方法内部");
	}
}

public class DeadLock implements Runnable {
	A a = new A();
	B b = new B();

	public void init() {
		Thread.currentThread().setName("主线程");
		// 调用a对象的foo方法
		a.foo(b);
		System.out.println("进入了主线程之后");
	}

	public void run() {
		Thread.currentThread().setName("副线程");
		// 调用b对象的bar方法
		b.bar(a);
		System.out.println("进入了副线程之后");
	}

	public static void main(String[] args) {
		DeadLock dl = new DeadLock();
		new Thread(dl).start();

		dl.init();
	}
}

解决方法:

  • 专门的算法、原则
  • 尽量减少同步资源的定义
  • 尽量避免嵌套同步

Lock锁方式解决线程安全问题

 * 1. 面试题:synchronizedLock的异同?
 *   相同:二者都可以解决线程安全问题
 *   不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
 *        Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock()*
 * 2.优先使用顺序:
 * Lock  同步代码块(已经进入了方法体,分配了相应资源)  同步方法(在方法体之外)
 *
class Window implements Runnable{
    private int ticket = 100;
    //1、实例化ReentrantLock
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true) {
            try {
                //2、调用锁定方法lock()
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
                    ticket--;
                } else break;
            }finally {
                //3、调用解锁方法:unlock()
                lock.unlock();
            }
        }
    }
}
public class LockTest {
    public static void main(String[] args) {
        Window w = new Window();
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

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

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

    }
}
//E:\java\javaSE\thread\src\com\pan\test1\LockTest.java

练习

 * 银行有一个账户。
 *有两个储户分别向同一个账户存2000元,每次存10,存200次。每次存完打印账户余额。
class Custmer implements Runnable{
    private double balance = 0;
    @Override
    public void run(){
        for(int i = 0; i < 200; i++){
            deposit(10);
        }
    }
    public synchronized void deposit(double amt){
        if(amt > 0){
            balance += amt;
            try{
                Thread.sleep(1);

            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":存钱成功。余额为:" + balance);
        }

    }
}

public class AccountTest {
    public static void main(String[] args) {
        Custmer custmer = new Custmer();

        Thread t1 = new Thread(custmer);
        Thread t2 = new Thread(custmer);

        t1.start();
        t2.start();
    }
}
//E:\java\javaSE\thread\src\com\pan\exer\AccountTest.java

线程的通信

栗子:使用两个线程打印1-100,线程1和2交替打印。

 * 涉及到的三个方法:
 * wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器,其他线程可以拿到同步监视器进入同步代码块中。
 * notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
 * notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
 *
 * 说明:
 * 1.wait()notify()notifyAll()三个方法必须使用在同步代码块或同步方法中。
 * 2.wait()notify()notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
 *    否则,会出现IllegalMonitorStateException异常
 * 3.wait()notify()notifyAll()三个方法是定义在java.lang.Object类中。
 *
 * 面试题:sleep()wait()的异同?
 * 1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
 * 2.不同点:1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
 *          2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
 *          3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
 *
class Number implements Runnable{
    private int number = 1;
    @Override
    public void run() {
        while(true){
            synchronized (this) {
                this.notify();
                if(number <= 100){
                    try{
                        Thread.sleep(10);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+ ":" + number);
                    number++;
                    try {
                        this.wait();//使调用wait()的方法进入阻塞状态
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                else{
                    break;
                }
            }
        }
    }
}
public class CommunicationTest {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);

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

        t1.start();
        t2.start();
    }
}
//E:\java\javaSE\thread\src\com\pan\exer\CommunicationTest.java

创建多线程的方式三:实现Callable接口

在这里插入图片描述

//1.创建一个实现Callable的实现类
class NumThread implements Callable{

    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for(int i = 1; i < 101; i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
                sum += i;
            }
        }
        return sum;
    }
}

public class ThreadNew {
    public static void main(String[] args) {
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();
        //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numThread);
        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        new Thread(futureTask).start();
        try {
            //6.获取Callable中call方法的返回值
            //get()返回值就是FutureTask构造器参数Callable实现重写的call()的返回值
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

创建多线程的方式四:使用线程池

在这里插入图片描述

package com.pan.test1;

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

class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 101; i++) {
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

class NumberThread2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 101; i++) {
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadPool {
    public static void main(String[] args) {
        //1.提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);

        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();

        //2.执行指定的线程操作,需要提供实现Runnable接口或者Callable接口实现类的对象
        service.execute(new NumberThread1());//适用于Runnable
        service.execute(new NumberThread2());
        //service.submit(Callable callable);//适用于Callable
        //3.关闭连接池
        service.shutdown();
    }
}

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

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

相关文章

结构体熟练掌握--实现通讯录

魔王的介绍&#xff1a;&#x1f636;‍&#x1f32b;️一名双非本科大一小白。魔王的目标&#xff1a;&#x1f92f;努力赶上周围卷王的脚步。魔王的主页&#xff1a;&#x1f525;&#x1f525;&#x1f525;大魔王.&#x1f525;&#x1f525;&#x1f525; ❤️‍&#x1…

帮助中心在线制作工具推荐这4款,很不错哟!

根据用户咨询问题是否解决的情景&#xff0c;分为三个部分&#xff0c;首先帮助中心恰好有用户需要咨询的问题&#xff0c;用户可以通过点击相关问题即可解决自己的问题&#xff0c;其次&#xff0c;用户第一眼没有在帮助中心解决问题&#xff0c;有个搜索框&#xff0c;用户的…

为什么开发人员应该在 2023 年学习 Docker 和 Kubernetes

开发者你好&#xff0c;如果你想在 2023 年学习新的工具和技术&#xff0c;那么你应该考虑学习 Docker 和 Kubernetes&#xff0c;这是在这个微服务和云计算时代创建和管理容器的两个最重要的工具。随着微服务和云计算的兴起&#xff0c;Docker 和 Kubernetes 已经成为软件开发…

gg又来深圳

我们都喜欢的DGGgg是我在TCL的朋友&#xff0c;刚毕业的我们在TCL度过了一段非常欢快的时光&#xff0c;gg也是我们几十人中在TCL呆的特别久的&#xff0c;先是在深圳&#xff0c;然后转去了惠州&#xff0c;后面在惠州买房、结婚、定居、生娃。前几年举家回了西安、也从TCL离职…

ChatGPT到底是个啥 - 它甚至会和狗说话

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

2023.2.12(总结)

今天主要就是下午进行了一个测试&#xff0c;有三个困难版的题目我没有写出来&#xff0c;打算今天晚上好好磨磨&#xff0c;这里主要就只放一个题目。 C - Don’t be cycle Editorial / Time Limit: 2 sec / Memory Limit: 1024 MB Score : 300300 points Problem Statemen…

Spring缓存指定Redis做为缓存中间件Demo笔记

文档地址D:/Test10/redisdemo和springcachedemo E:/FTPshangchuang/smbms 一下是SpringBoot整合Redis的初略配置,引入Redis依赖 想自己测试的话 链接&#xff1a;https://pan.baidu.com/s/14hdBzdjtFu0lYmZUhy_DuA 提取码&#xff1a;j0m8 Redis配置文件 package com…

总览 Java 容器--集合框架的体系结构

前言 我们在讲 Java 的数据类型的时候&#xff0c;单独介绍过数组&#xff0c;数组也确实是开发程序中常用的内存类型之一&#xff0c;不过 Java 内置的数组限制颇多&#xff0c;所以此后扩展出了List这种结构&#xff0c;与之类似的Set、Queue 这些内存中的容器都被放在了 Co…

浅谈明暗水印

前言 水印&#xff08;Watermark&#xff09;是一种能让人识别纸上图案的技术&#xff0c;当光线照射纸张时&#xff0c;纸张上会显现出各种不同阴影&#xff0c;这些阴影组成的图案就是水印。 水印常常起到验证货币、护照、邮票、政府文件或者其他纸制文件的真实性的作用。 …

什么是热迁移?90%的企业都理解错误

科技的发展&#xff0c;新冠的冲击&#xff0c;让市场竞争愈发激烈。尽管云计算服务为企业免除了基础硬件的建设和维护成本&#xff0c;当企业需要进行业务跨架调整、升级维护、环境测试等场景而进行云迁移&#xff0c;其过程中所带来的停机时间&#xff0c;就变得尤为头疼了。…

清亡之路(4):最受误解的东南互保

很多人一提“东南互保”&#xff0c;就认为是东南是在反叛。如果仔细看&#xff0c;其实根本谈不上造反&#xff0c;反而是更像是一种“遵旨行事”。本文就是说说这个问题。宣战是来真的吗&#xff1f;1900年6月21日&#xff0c;慈禧忍无可忍&#xff0c;决定和各公使馆翻脸&am…

给你的边框加点渐变

目录前言border-imageborder-image实现background父子divbackgorund一个div一个伪元素background-clip&#x1f9e8;&#x1f9e8;&#x1f9e8; 大家好&#xff0c;我是搞前端的半夏 &#x1f9d1;&#xff0c;一个热爱写文的前端工程师 &#x1f4bb;. 如果喜欢我的文章&…

spring cloud

文章目录 目录 文章目录 前言 一、spring cloud 二、ribbon负载均衡 三、openfeign 总结 前言 微服务就是一种将一个单一应用程序拆分为一组小型服务的方法&#xff0c;拆分完成后&#xff0c;每一个服务都运行在独立的进程中&#xff0c;服务于服务之间采用轻量级的通信机制来…

我不允许你还不知道CSS的filter的drop-shadow阴影用法以及与box-shadow的区别详解

这里有两个图片的阴影&#xff0c;你觉得哪个好看&#xff1f; 一个是使用box-shadow另一个是使用filter: drop-shadow 一、我们来了解一下CSS的filter&#xff08;过滤器&#xff09; 该CSS的filter属性可以实现很多效果 &#xff08;一&#xff09;filter: blur(5px) // 高…

Elasticsearch索引库和文档的相关操作

前言&#xff1a;最近一直在复习Elasticsearch相关的知识&#xff0c;公司搜索相关的技术用到了这个&#xff0c;用公司电脑配了环境&#xff0c;借鉴网上的课程进行了总结。希望能够加深自己的印象以及帮助到其他的小伙伴儿们&#x1f609;&#x1f609;。 如果文章有什么需要…

if从入门到出轨

if从入门到出轨(java版本) 为什么会产生很多if分支 在我们的日常生活中,会遇到很多判断逻辑,屁如,如果你在2月14号,心情很好,那么就给女朋友买了个iPhone 14 Pro Max 1TB 银白色,如果你女朋友在2月14号没有收到您老人家的礼物,那么你可能睡沙发或者轨搓衣板,或者直接和其他帅…

【Kafka】【十九】新消费组的消费offset规则

新消费组的消费offset规则 新消费组中的消费者在启动以后&#xff0c;默认会从当前分区的最后⼀条消息的offset1开始消费&#xff08;消费新消息&#xff09;。可以通过以下的设置&#xff0c;让新的消费者第⼀次从头开始消费。之后开始消费新消息&#xff08;最后消费的位置的…

电脑分盘怎么合并?只需1分钟,轻松学会

有些小伙伴喜欢将电脑进行分盘&#xff0c;以此将文件放进不同的分盘进行管理。但有时候&#xff0c;电脑磁盘分盘过多&#xff0c;管理起来又会有些麻烦。将一些闲置的磁盘进行合并很有必要。电脑分盘怎么合并&#xff1f;下面就跟着小编一起来看看吧。 电脑分盘怎么合并&…

Java 变量和数据类型,超详细整理,适合新手入门

目录 一、什么是变量&#xff1f; 二、变量 变量值互换 三、基本数据类型 1、八种基本数据类型 2、布尔值 3、字符串 四、从控制台输入 一、什么是变量&#xff1f; 变量是一种存储值的容器&#xff0c;它可以在程序的不同部分之间共享&#xff1b;变量可以存储数字、字…

二维数组的定义

1. 概念二维数组就是一种数组的数组&#xff0c;其本质上还是一个一维数组&#xff0c;只是它的数据元素又是一个一维数组。如果你对这个概念想象不出来&#xff0c;给大家举个栗子&#xff0c;相信吸烟的同学一下子就会明白。一根烟 一个变量一包烟 20根烟 一维数组一条烟 …