当前位置:   article > 正文

java总结_updatefixedpoint(list)' in 'com.jaxf.serv

updatefixedpoint(list)' in 'com.jaxf.service.impl.deviceserviceimp

/Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home

Springboot注解

@RequestParam
@RequestBody
@RequestParam Long mailConfigId,
@RequestParam(required = false) String pt,
@RequestParam(required = false) Integer limit,
@RequestBody(required = false) Map<String, String> orderMap
  • 1
  • 2
  • 3
  • 4

异步注解@Async

同步(Synchronous):特指在同一个线程里面执行的内容之间的关系是有顺序的;线程之间需要同步时,就需要使用一些显式的同步方式,这也是多线程带来的大问题,在多线程编程需要特别关注。

异步(Asynchronous):特指一些执行内容可以不用直接等待运行结果,异步执行的内容一般是在另一个线程运行,只需要运行完结果反馈回来就行。

img

这个注解的作用在于可以让被标注的方法异步执行,但是有两个前提条件

  1. 配置类上添加 @EnableAsync 注解

  2. 需要异步执行的方法的所在类由Spring管理

  3. 需要异步执行的方法上添加了 @Async 注解

Demo

多线程与高并发

基本概念

进程

**进程:**每一个进程都占有 CPU、内存、磁盘、网络等资源。站在操作系统的角度,进程是分配资源的基本单位,也是最小单位

从 CPU 角度,执行过程是这样子的:CPU 一直在负责执行指令,进程之间互相竞争 CPU 资源,下图有 A 和 B 进程,在一个时间点,CPU 只执行一个进程的指令,因为 CPU 运行很快,所以在咱们看起来,像是多个进程在同时跑。这就是进程带来的好处:提高资源利用率,并发地执行多个程序

当然引入进程也不是有益无害,它增加了系统的时间空间开销。空间开销这个好理解,进程有自己的组成部分(下面会讲),这个就占用了空间。时间开销则是进程切换需要时间。

img

串行(Serial)**、并行(Parallel)、**并发(Concurrent)

串行(Serial)

现在我们公司附近的快餐,人少的时候,就是排一条队,如下图所示,每个人按顺序排,一直往前走,如果看到想吃的菜,就用手指一指,快餐员工就会给你打菜,这个应该是很多快餐店都采用的方式,容易管理,但是有一点需要强调的就是如果一个同学只想吃米饭和豆芽,他还是需要排一整队到最后的结账台结账。这其实就是咱们计算机世界里面的串行,一条队伍,依次有序的执行着。

串行

并行(Parallel)

不过一到 12 点高峰期上面那种排队方式就撑不住了,分分钟排队排到外面晒太阳,这时为了提高效率,因为快餐店还有一片空地,所以又加了一套打菜装备和员工,这时很好的解决了一条队伍太长的问题,这时就是并行了,2 套打菜装备和员工各自互不干涉,完全独立,每套装备能够解决每个顾客的点菜需求。当然这要求比较高,需要餐厅有足够的空间可以放下两套装备,并且需要雇佣多一倍的员工来为顾客打菜,这里就类似计算机的双核

并行

并发(Concurrent)

除了上面的两种快餐排队方式,还见过下面这种的,只有一套打菜装备,但是却有很多个队,每个菜排一条队(现实中其实不止一个菜,而是多个菜,这里画图就简化成一个菜),每个人就只需要排自己想吃的菜的队伍,这一道道菜就像计算机的各个资源,比如 CPU、IO 等等,人就像一个一个进程,有些只需要 CPU 计算资源,有些还要 IO 资源,各取所需,这种方式就是并发。这种打菜方式和上面第一种所需要的资源是一样的:一套打菜装备和员工。每个顾客最终都需要走到结账这个步骤,而结账只有一个,也就是类似计算机的单核。

并发

总结

那么回归到计算机世界,这三者是什么东西呢?

早期计算机只有一个 CPU,也就是所谓的单核计算机,只有一个 CPU 去执行任务,所以只能是一个任务一个任务的跑,每个任务跑完才让下一个任务跑,也就是串行的。

后面因为进程和线程概念的提出与实现,它们使得任务以进程的方式运行,拆分成多个时间片运行,而不是一次运行执行完任务,这样子在一个 CPU 运行中可以有多个任务在并发执行,这时还是只有一个 CPU,一个时间点只有一个任务在执行。

并行是因为 CPU 硬件的发展,出现多核 CPU,所以实现了真正的同一时间点能有多个任务在执行

阻塞(Blocking)和非阻塞(Non-blocking)

如果某一个需求点阻塞了,应该就先做手头上其他工作,如果手头上没其他工作,就跟老板反馈情况后领其他任务做,还要时刻去跟进阻塞的需求点的进度。

阻塞

阻塞

非阻塞

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YpV3zULU-1595785774605)(http://www.liebrother.com/upload/dd2c2cb241de4733afe6ad1610189194_02.jpg)]

线程

试想一下没有线程的程序是怎么样的?百度网盘在上传文件时就无法下载文件了,得等文件上传完成后才能下载文件。这个我们现在看起来很反人性,因为我们习惯了一个程序同时可以进行运行多个功能,而这些都是线程的功劳。

线程的组成

线程ID:线程标识符。

当前指令指针(PC):指向要执行的指令。

寄存器集合:存储单元寄存器的集合。

堆栈:暂时存放数据和地址,一般用来保护断点和现场。

线程与进程区别

线程和进程之间的区别,我觉得可以用这个例子来看出两者的不同,进程就是一栋房子,房子住着 3 个人,线程就是住在房子里的人。进程是一个独立的个体,有自己的资源,线程是在进程里的,多个线程共享着进程的资源。

线程状态

我们看到 Java 源代码里面,线程状态的枚举有如下 6 个。

public enum State {

 //新建状态
 NEW,

 //运行状态
 RUNNABLE,

 //阻塞状态
 BLOCKED,

 //等待状态
 WAITING,

 //等待状态(区别在于这个有等待的时间)
 TIMED_WAITING,

 //终止状态
 TERMINATED;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

NEW:新建状态。在创建完 Thread ,还没执行 start() 之前,线程的状态一直是 NEW。可以说这个时候还没有真正的一个线程映射着,只是一个对象。

RUNNABLE:运行状态。线程对象调用 start() 之后,就进入 RUNNABLE 状态,该状态说明在 JVM 中有一个真实的线程存在。

BLOCKED:阻塞状态。线程在等待锁的释放,也就是等待获取 monitor 锁。

WAITING:等待状态。线程在这个状态的时候,不会被分配 CPU,而且需要被显示地唤醒,否则会一直等待下去。

TIMED_WAITING:超时等待状态。这个状态的线程也一样不会被分配 CPU,但是它不会无限等待下去,有时间限制,时间一到就停止等待。

TERMINATED:终止状态。线程执行完成结束,但不代表这个对象已经没有了,对象可能还是存在的,只是线程不存在了。

线程既然有这么多个状态,那肯定就有状态机,也就是在什么情况下 A 状态会变成 B 状态。下面就来简单描述一下。

结合下图,我们 new 出线程类的时候,就是 NEW 状态,

调用 start() 方法,就进入了 RUNNABLE 状态,

这时如果触发等待,则进入了 WAITING 状态,

如果触发超时等待,则进入 TIMED_WAITING 状态,

当访问需要同步的资源时,则只有一个线程能访问,其他线程就进入 BLOCKED 状态,

当线程执行完后,进入 TERMINATED 状态。

图片来源于网路,侵删

其实在 JVM 中,线程是有 9 个状态,如下所示,有兴趣的同学可以深入了解一下。

javaClasses.hpp
enum ThreadStatus {
    NEW = 0,
    RUNNABLE = JVMTI_THREAD_STATE_ALIVE + // runnable / running
                               JVMTI_THREAD_STATE_RUNNABLE,
    SLEEPING = JVMTI_THREAD_STATE_ALIVE + // Thread.sleep()
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
                               JVMTI_THREAD_STATE_SLEEPING,
    IN_OBJECT_WAIT = JVMTI_THREAD_STATE_ALIVE + // Object.wait()
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_INDEFINITELY +
                               JVMTI_THREAD_STATE_IN_OBJECT_WAIT,
    IN_OBJECT_WAIT_TIMED = JVMTI_THREAD_STATE_ALIVE + // Object.wait(long)
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
                               JVMTI_THREAD_STATE_IN_OBJECT_WAIT,
    PARKED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park()
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_INDEFINITELY +
                               JVMTI_THREAD_STATE_PARKED,
    PARKED_TIMED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park(long)
                               JVMTI_THREAD_STATE_WAITING +
                               JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
                               JVMTI_THREAD_STATE_PARKED,
    BLOCKED_ON_MONITOR_ENTER = JVMTI_THREAD_STATE_ALIVE + // (re-)entering a synchronization block
                               JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER,
    TERMINATED = JVMTI_THREAD_STATE_TERMINATED
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

Java 线程实现

下面讲一讲在 Java 中如何创建一个线程。众所周知,实现 Java 线程有 2 种方式:继承 Thread 类和实现 Runnable 接口。

继承 Thread 类

继承 Thread 类,重写 run() 方法。

class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("MyThread");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
实现 Runnable 接口

实现 Runnable 接口,实现 run() 方法。

class MyRunnable implements Runnable {

    public void run() {
        System.out.println("MyRunnable");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这 2 种线程的启动方式也不一样。

MyThread 是一个线程类,所以可以直接 new 出一个对象出来,接着调用 start() 方法来启动线程;

MyRunnable 只是一个普通的类,需要 new 出线程基类 Thread 对象,将 MyRunnable 对象传进去。

下面是启动线程的方式。

public class ThreadImpl {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread myRunnable = new Thread(new MyRunnable());
        System.out.println("main Thread begin");
        myThread.start();
        myRunnable.start();
        System.out.println("main Thread end");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

打印结果如下:

main Thread begin
main Thread end
MyThread
MyRunnable
  • 1
  • 2
  • 3
  • 4

看这结果,不像咱们之前的串行执行依次打印,主线程不会等待子线程执行完。

敲重点:不能直接调用 run(),直接调用 run() 不会创建线程,而是主线程直接执行 run() 的内容,相当于执行普通函数。这时就是串行执行的。看下面代码。

public class ThreadImpl {

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread myRunnable = new Thread(new MyRunnable());
        System.out.println("main Thread begin");
        myThread.run();
        myRunnable.run();
        System.out.println("main Thread end");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

打印结果:

main Thread begin
MyThread
MyRunnable
main Thread end
  • 1
  • 2
  • 3
  • 4

从结果看出只是串行的,但看不出没有线程,我们看下面例子来验证直接调用 run() 方法没有创建新的线程,使用 VisualVM 工具来观察线程情况。

我们对代码做一下修改,加上 Thread.sleep(1000000) 让它睡眠一段时间,这样方便用工具查看线程情况。

调用 run() 的代码:

public class ThreadImpl {

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("MyThread");
        Thread myRunnable = new Thread(new MyRunnable());
        myRunnable.setName("MyRunnable");
        System.out.println("main Thread begin");
        myThread.run();
        myRunnable.run();
        System.out.println("main Thread end");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("MyThread");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class MyRunnable implements Runnable {

    public void run() {
        System.out.println("MyRunnable");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

运行结果:

main Thread begin
MyThread
  • 1
  • 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1mmwD1h4-1595785774608)(http://www.liebrother.com/upload/68ae1f24617f4cde8bf1f3382db17e42_dxc_0005_01.jpg)]

只打印出 2 句日志,观察线程时也只看到 main 线程,没有看到 MyThreadMyRunnable 线程,印证了上面咱们说的:直接调用 run() 方法,没有创建线程

下面我们来看看有 调用 start() 的代码:

public class ThreadImpl {

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("MyThread");
        Thread myRunnable = new Thread(new MyRunnable());
        myRunnable.setName("MyRunnable");
        System.out.println("main Thread begin");
        myThread.start();
        myRunnable.start();
        System.out.println("main Thread end");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

运行结果:

main Thread begin
main Thread end
MyThread
MyRunnable
  • 1
  • 2
  • 3
  • 4

img

所有日志都打印出来了,并且通过 VisualVM 工具可以看到 MyThreadMyRunnable 线程。看到了这个结果,切记创建线程要调用 start() 方法。

线程安全的停止

1、线程自然终止:自然执行完或抛出未处理异常

2、stop(),resume(),suspend()已不建议使用,stop()会导致线程不会正确释放资源,suspend()容易导致死锁。

3、使用interrupt()方法

java线程是协作式,而非抢占式

调用一个线程的interrupt() 方法中断一个线程,并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。

isInterrupted() 判定当前线程是否处于中断状态。

static方法interrupted() 判定当前线程是否处于中断状态,同时中断标志位改为false。

方法里如果抛出InterruptedException,线程的中断标志位会被复位成false,如果确实是需要中断线程,要求我们自己在catch语句块里再次调用interrupt()。

代码如下

import java.util.concurrent.ExecutionException;

/**
 * @Auther: BlackKingW
 * @Date: 2019/4/14 12:09
 * @Description:
 */
public class DaemonThread {


	private static class UseThread extends Thread {
		@Override
		public void run() {
			try {
				while (!isInterrupted()) {
					System.out.println(Thread.currentThread().getName()
							+ " I am extends Thread.");
				}
				System.out.println(Thread.currentThread().getName() 
						+ " interrupt flag is " + isInterrupted());
			} finally {
				System.out.println("...........finally");
			}
		}
	}

	public static void main(String[] args) throws InterruptedException, 
		ExecutionException {
		UseThread useThread = new UseThread();
		useThread.start();
		Thread.sleep(5);
		useThread.interrupt();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

线程间的共享

1、synchronized内置锁

对象锁,锁的是类的对象实例。

类锁 ,锁的是每个类的的Class对象,每个类的的Class对象在一个虚拟机中只有一个,所以类锁也只有一个。

2、volatile关键字

适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。

3、ThreadLocal

线程变量。可以理解为是个map,类型 Map<Thread,Integer>

线程间协作

1、轮询

难以保证及时性,资源开销很大,

2、等待和通知

wait() 对象上的方法,将是当前执行线程进行等待。

notify/notifyAll 对象上的方法 发送信号量,唤醒线程。

代码示例:

git@github.com:nignmengcc/spring_boot_demo.git
TestWN
  • 1
  • 2
/**
 * @author didi
 */
public class Express {
    public final static String CITY = "ShangHai";
    /**快递运输里程数*/
    private int km;
    /**快递到达地点*/
    private String site;

    public Express() {
    }

    public Express(int km, String site) {
        this.km = km;
        this.site = site;
    }

    /** 变化公里数,然后通知处于wait状态并需要处理公里数的线程进行业务处理
    * */
    public synchronized void changeKm(Integer km){
        this.km = km;
        notifyAll();
        //其他的业务代码

    }

    /** 变化地点,然后通知处于wait状态并需要处理地点的线程进行业务处理*/
    public synchronized void changeSite(String site){
        this.site = site;
        notify();
    }

    public synchronized void waitKm(){
        while(this.km<=100) {
            try {
                wait();
                System.out.println("check km thread["+Thread.currentThread().getId()
                        +"] is be notifed.");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("the km is"+this.km+",I will change db.");

    }

    public synchronized void waitSite(){
        while(CITY.equals(this.site)) {
            try {
                wait();
                System.out.println("check site thread["+Thread.currentThread().getId()
                        +"] is be notifed.");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("the site is"+this.site+",I will call user.");
    }
}







/**
 * @author didi
 */
public class TestWN {
    /**
     * 静态类express
     * */
    private static Express express = new Express(0,Express.CITY);


    /**检查里程数变化的线程,不满足条件,线程一直等待*/
    private static class CheckKm extends Thread{
        @Override
        public void run() {
//            express.waitKm();
            synchronized (express) {
                while(express.getKm()<=100) {
                    try {
                        express.wait();
                        System.out.println("check km thread["+Thread.currentThread().getId()
                                +"] is waiting be notifed.");

                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                System.out.println("the km is"+express.getKm()+",I will change db.");
            }
        }

    }

    /**检查地点变化的线程,不满足条件,线程一直等待*/
    private static class CheckSite extends Thread{
        @Override
        public void run() {
            express.waitSite();
        }

    }

    public static void main(String[] args) throws InterruptedException {

        /**三个检测地点等待线程*/
        for(int i=0;i<3;i++){
            new CheckSite().start();
        }
        /**三个检测里程数等待线程*/
        for(int i=0;i<3;i++){
            new CheckKm().start();
        }

        Thread.sleep(1000);

        /**快递里程变化*/
//        express.changeKm(1000);
        synchronized (express) {
            express.setKm(1000);
            express.notifyAll();
        }

        /**快递里程变化*/
        express.changeSite("HangZhou");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136

等待和通知的标准范式

等待方:

  1. 获取对象的锁;
  2. 判断条件是否满足,不满足调用wait方法,继续等待
  3. 条件满足执行业务逻辑

通知方来说

  1. 获取对象的锁;
  2. 改变条件
  3. 通知所有在等待对象的线程

notify和notifyAll应该用谁?

应该尽量使用notifyAll,使用notify因为有可能发生信号丢失的的情况

notify()方法只能随机唤醒一个线程。

那么顾名思义,notifyAll()就是用来唤醒正在等待状态中的所有线程的,不过也需要注意以下几点:
(1)notifyAll()只会唤醒那些等待抢占指定object’s monitor的线程,其他线程则不会被唤醒。
(2)notifyAll()只会一个一个的唤醒,而并非统一唤醒。因为在同一时间内,只有一个线程能够持有object’s monitor
(3)notifyAll()只是随机的唤醒线程,并非有序唤醒。

3、join()方法

thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。

比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

代码示例:

/**
 * @author didi
 */
public class UseJoin {
    static class JumpQueue implements Runnable {
        private Thread thread;//用来插队的线程

        public JumpQueue(Thread thread) {
            this.thread = thread;
        }

        @Override
        public void run() {
            try {
                System.out.println(thread.getName()+" will be join before "
                        +Thread.currentThread().getName());
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" terminted.");
        }
    }

    public static void main(String[] args) throws Exception {
        //现在是主线程
        Thread previous = Thread.currentThread();
        for (int i = 0; i < 10; i++) {
            //i=0,previous 是主线程,i=1;previous是i=0这个线程
            Thread thread =
                    new Thread(new JumpQueue(previous), String.valueOf(i));
            System.out.println(previous.getName()+" jump a queue the thread:"
                    +thread.getName());
            thread.start();
            previous = thread;
        }
        //让主线程休眠2秒
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName() + " terminate.");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

问题:调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?

线程在执行yield()以后,持有的锁是不释放的

sleep()方法被调用以后,持有的锁是不释放的

调动方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁

调动方法之前,必须要持有锁,调用notify()方法本身不会释放锁的

线程的并发工具类

一、Fork-Join框架

1、分而治之

规模为N的问题,N<阈值,直接解决,N>阈值,将N分解为K个小规模子问题,子问题互相对立,与原问题形式相同,将子问题的解合并得到原问题的解

动态规范

img

2、工作密取 workStealing

就是在任务分割的时候,前面的任务执行可能会比后面的执行速度快,当前面的执行完,后面的还没执行的时候,执行完前面的任务的线程不会停止,而是从后面的任务的尾部取出子任务继续工作。Fork-Join就是实现了这样的机制。

Fork/Join 使用的标准范式

img

二、常用的并发工具类

1、CountDownLatch

作用:是一组线程等待其他的线程完成工作以后在执行,加强版join

await用来等待,countDown负责计数器的减一

代码示例:

5、Callable**、Future****和FutureTask**

类之间的关系

img

isDone,结束,正常还是异常结束,或者自己取消,返回true;

isCancelled 任务完成前被取消,返回true;

cancel(boolean):

  1. 任务还没开始,返回false
  2. 任务已经启动,cancel(true),中断正在运行的任务,中断成功,返回true,cancel(false),不会去中断已经运行的任务
  3. 任务已经结束,返回false

线程池的使用与分析

一、什么是线程池?为什么要用线程池?

线程池(thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。

优势:

  1. 降低资源的消耗。降低线程创建和销毁的资源消耗;
  2. 提高响应速度。例如:线程的创建时间为T1,执行时间T2,销毁时间T3,免去T1和T3的时间
  3. 提高线程的可管理性。

二、如何实现一个线程池

根据线程池的概念,如果要自己创建线程池,应该满足一下条件。

  1. 保存线程的容器。因为线程必须在池子已经创建好了,并且可以保持住,因此,需要一个容器去保存我们的线程。
  2. 可以接受外部任务。线程还要能够接受外部的任务,冰并运行这个任务。
  3. 保存任务的容器,有些任务可能来不及执行,因此需要将来不及执行的任务通过容器保存起来。

根据以上的条件以及之前我们学的并发编程知识,我们先手动自己尝试写一个线程池

Code:

1、定义WorkThread类,用来表示执行的线程,用于监听阻塞队列任务。

2、创建构建函数,我们将线程池进行初始化,并启动所有的工作线程。workThreads用来保存运行的线程,使用BlockingQueue taskQueue用来保存我们的任务队列

3、创建提交任务方法execute,用于提交我们的任务。

4、创建销毁线程池的方法destroy,用于销毁线程池。

之后我们编写测试类

三、JDK中的线程池和工作机制

我们大致了解了线程池的一个机制,那我们看下JDK中,是如何实现线程池的吧。

JAVA中,ThreadPoolExecutor,是所有线程池实现的父类,类结构图如下。

img

它的构造函数含有以下参数:

img

参数含义
int corePoolSize线程池中核心线程数,< corePoolSize ,就会创建新线程,= corePoolSize ,这个任务就会保存到BlockingQueue,如果调用prestartAllCoreThreads()方法就会一次性的启动corePoolSize 个数的线程。
int maximumPoolSize允许的最大线程数,如果BlockingQueue也满了,并且线程数< maximumPoolSize时候就会再次创建新的线程
long keepAliveTime线程空闲下来后,存活的时间,这个参数只在线程数> corePoolSize才有用
TimeUnit unit存活时间的单位值
BlockingQueue workQueue保存任务的阻塞队列
ThreadFactory threadFactory创建线程的工厂,给新建的线程赋予名字
RejectedExecutionHandler handler饱和策略AbortPolicy :直接抛出异常,默认;CallerRunsPolicy:用调用者所在的线程来执行任务DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务DiscardPolicy :当前任务直接丢弃实现自己的饱和策略只要实现RejectedExecutionHandler接口即可

提交任务

execute(Runnable command) 不需要返回

Future submit(Callable task) 需要返回值

关闭线程池

shutdown(),shutdownNow();

shutdownNow():设置线程池的状态,还会尝试停止正在运行或者暂停任务的线程

shutdown()设置线程池的状态,只会中断所有没有执行任务的线程

2、线程池的工作机制

img

1、如果工作线程数小于核心线程数,则创建工作线程

2、如果工作线程数等于或者大于核心线程数,则将任务提交到阻塞队列中

3、如果阻塞队列也满了,但线程数小于最大线程数,则创建新的线程

4、如果创建新的线程也满了,则执行任务饱和策略。

源码如下

img

3、如何合理配置线程池

根据任务的性质来:计算密集型(CPU),IO密集型,混合型

计算密集型:例如加密,大数分解,正则……等

推荐:机器的Cpu核心数+1,为什么+1,防止页缺失,(机器的Cpu核心=Runtime.getRuntime().availableProcessors()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/292226
推荐阅读
相关标签