JavaSE笔记_13

多线程

一、相关概念

  • 并发(Concurrent):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
    • 当有多个线程在操作时,如果系统只有一个CPU,则不可能真正同时进行多个线程,而是在不同线程中来回切换【同一时间只有一个线程在执行
  • 并行(Parallel):在操作系统中是指,一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。对比地,并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)。
    • 当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行。其实决定并行的因素不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行。【同一时间可以有多个线程在执行
  • 进程(Process):是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的 基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
    • 用来分配资源,一个进程可以有多个线程,但至少有一个线程
    • 进程执行时的间断性,决定了进程可能具有多种状态
      • 就绪状态(Ready) : 等待分配处理器资源
      • 运行状态 (Running) : 进程占用处理器资源
      • 阻塞状态 (Blocked) : 由于进程等待某种条件, 在条件满足之前无法继续执行 |eg:等待键盘输入|
  • 线程(Thread):是操作系统能够进行运算调度的 最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
    • 线程是进程的一部分,一个线程只能属于一个进程

【线程状态】

NEW:新建
        创建线程任务。
RUNNABLE:就绪(运行)
        线程调用start()方法。  CPU执行权 && 锁对象
BLOCKED:阻塞
        线程没有CPU执行权 或者 没有获得锁对象。
WAITING:无限等待(wait)
        调用wait()方法,进入该状态,
        直到该锁对象上的另一个线程,调用notify()/notifyAll(),将其唤醒,才能执行。
TIMED_WAITING:计时等待
        调用sleep(time),时间到了,自动醒来执行。
TERMINATED:死亡
        1. run()方法执行完毕。
        2. 调用stop()方法。

注意:
   sleep()和wait()的区别:
        sleep():
            1. Thread类中的方法
            2. 可以在任意处使用
            3. 【不会释放锁对象】
            4. 时间到了,自动醒来
         wait():
            1. Object类中的方法
            2. 只能在 synchornized 中使用
            3. 【会释放锁对象】
            4. 必须等待同一个锁对象上的其它线程,将其唤醒。

二、线程池

  1. 池类技术
  2. 提高线程的复用性,节约系统资源
  3. 线程使用完毕,不会销毁线程,而是归还到线程池,可以复用

作用:
1. 降低系统资源消耗
2. 对线程数量可控
3. 提高用户响应效率

4. 四大线程池:

            1. newCachedThreadPool()              无边界线程
                ExecutorService es = Executors.newCachedThreadPool();
                        es.submit(p);
            2. newFixedThreadPool(int nThreads)   有边界线程池
            3. newScheduledThreadPool(int corePoolSize)    延迟线程池
                ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);
                Runnable r = () -> System.out.println("张彪大衰哥!!!");
                // ses.schedule(r,5, TimeUnit.SECONDS);
                // 2 表示延迟两秒
                // 5 表示五秒后再次执行
                ses.scheduleWithFixedDelay(r,2,5,TimeUnit.SECONDS);
            4. newSingleThreadExecutor()            单例线程池
                执行一些需要顺序执行的线程任务。A -> B -> C -> D


        自定义线程池:
            ThreadPoolExecutor(
                int corePoolSize,     -- 核心线程数
                int maximumPoolSize,  -- 最大线程数
                long keepAliveTime,   -- 空闲线程存活时间
                TimeUnit unit,        -- 时间单位
                BlockingQueue workQueue,   -- 阻塞队列
                ThreadFactory threadFactory,         -- 创建线程方式
                RejectedExecutionHandler handler     -- 拒绝策略

            注意:
                空闲线程,不是核心线程全部占用后,就创建。
                而是核心线程数被全部占用,且阻塞队列中也满了,才会创建空闲线程。

            ThreadPoolExecutor.AbortPolicy:   默认,拒绝并抛出异常
            ThreadPoolExecutor.CallerRunsPolicy   拒绝,绕过线程池,由提交线程任务的线程负责执行。
            ThreadPoolExecutor.DiscardOldestPolicy   拒绝阻塞队列中,等待时间最长的线程任务,将新任务加入队列等待
            ThreadPoolExecutor.DiscardPolicy   直接拒绝,不抛异常
                )

三、多线程三大原则

  1. 原子性: 对基本数据类型变量的读和写是要保证原子性的,要么全部成功,要么全部失败,这些操作都是不可中断的
  • 多线程操作中,可能会因CPU抢占机制导致操作被其他线程中断,从而破坏其原子性。
  • 解决方法:使用 synchronized(同步代码块)、Lock锁或AtomicInteger
    • AtomicInteger: 数据持久化前,把该线程所操作的原数据与操作时使用的旧数据做比较,相同则将修改后的值赋值给主内存;不相同则重新操作
      • AtomicInteger可能会产生ABA问题(操作数据被其他线程操作过,但其值与旧值相同)
      • eg:你离开了座位,水杯留在了原地,我喝水拿错了杯子,发现后吐了回去。你回来后,水还是这杯水,但被我喝过了。

                乐观锁:加上版本号、时间戳。
                    乐观的认为我在操作的时候,没有其它人会操作,所以我不上锁。
                    但是加上版本号、时间戳来确保我数据(操作)安全。
                悲观锁:同步、lock。
                    悲观的任务我在操作的时候,一定会有其它人会操作,所以我一开始就上锁。
                    问题在于效率底。

                公平锁:Lock
                    新线程任务,在获取线程执行之前,会先判断阻塞队列中,是否有其它正在等待的线程。
                    如果有,则新任务会排队等待。
                    如果没有,则直接等待线程。
                非公平锁:Lock
                    新线程任务,在获取线程执行之前,不管理会队列中是否有其它线程等待,
                    而是同队列中第一个线程任务,抢夺线程执行权。

  1. 可见性: 一个线程修改了某一个共享变量的值时,其他线程是否能够立即知道这个修改
  • 多线程操作过程中,一个线程对主内存中的变量进行操作,另一个线程不是立即可见
    • **解决方法:**使用 synchronized(同步代码块)、Lock锁或volatile
      • volatile: 要求线程每次操作变量,都必须主动更新主内存的数据到本地内存。
  1. 有序性: 我们都知道java代码是逐句执行的
  • JVM运行我们的代码时,会根据自己的机制,对我们的代码进行指令重排(优化,因为我们写的太烂了)。
  • 解决方法: 使用 synchronized(同步代码块)、Lock锁或volatile,使其不进行指令重排

四、网络编程

  • 三要素
    1. ip:通信地址
    1. 端口:软件地址 范围:[0-65535),其中0-1024: 系统软件端口,不要占用
    1. 协议:通信规则
    • TCP协议
      • 面向链接,安全的(不会丢包)
      • 效率低,发送数据包大小不受限
    • UDP协议
      • 非面向连接,不安全的(可能会丢包)
      • 效率高,但发送数据包大小限制(64K)

你可能感兴趣的:(JavaSE笔记,java)