正文
一、JUC知识
1.进程与线程
进程:进程是后台运行的一个程序,它是操作系统动态执行的基本单元。比如QQ、微信
线程:线程是程序运行中的某个功能,一个进程包含若干个线程,线程是独立运行和独立调度的基本单位。比如一个QQ或微信的视频聊天
2.并发与并行
并发:同一个时间应对多件事情的能力
并行:同一个时间动手做多件事情的能力
单核cpu下,线程实际是 串行执行 操作系统中的一个组件叫做任务调度器,将CPU的时间片(windows 时间片最小约为 15 毫秒)分为不同的程序使用,只是由于cpu在线程间(时间片很短)切换的非常快,直觉上觉得是 同时运行 总结一句话就是:微观串行,宏观并行
一般将 线程轮流使用CPU 的做法称为并发(concurrent)
多核CPU下,每个 核(core)都可以调度运行线程,这时候线程可以是并行的
举例说明
你的一天,起床,吃饭、学习、吃饭、学习、刷手机、睡觉,一个人轮流交替做多件事,这就是并发
如果有一天,你发现自己拥有查克拉,你使出了传说中的影分身之术,分出一个分身,一起完成你的一天,这时既有并发,也有并行(当两个人同时要做吃饭这件事时,碗只有一个,你们就会产生竞争,你们看着办吧)
当你的影分身之术达到登峰造极之时(ps:有影分身还敲什么编程喽,整个中国我罩啦),那就可以有几个分身,同时处理某几件事(ps:这就叫专业)这就是并行
3.创建线程
注:直接调用run(),只会执行同一个线程中的任务,不会启动新线程,应该调用start()
实现 Runnable
继承 Thread
package io.laokou.test.concurrent; /** * @author Kou Shenhai * @version 1.0 * @date 2022/4/17 0017 下午 6:15 */ public class ThreadTest { public static void main(String[] args) { //lambda Runnable run = () -> { System.out.println("implements Runnable"); }; new Thread(run).start(); new MyThread().start(); } static class MyThread extends Thread { @Override public void run() { System.out.println("extends Thread"); } } } /** * implements Runnable * extends Thread */
4.线程状态
New:新创建状态,当一个线程处于新创建状态时,程序还没有开始运行线程中的代码
Runnable:可运行状态,一旦调用start(),线程处于runnable状态,一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行时间(一个正在运行中的线程仍然处于可运行状态),一旦一个线程开始运行,它不必始终保持运行,事实上,运行中的线程被中断,目的是为了让其他线程获取运行机会。线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行线程一个时间片执行任务,当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会,当选择下一个线程时,操作系统考虑线程的优先级。在多个处理器的机器上,每一个处理器运行一个线程,可以多个线程并发运行,如果线程数目多于处理器的数目,调度器依然采用时间片机制。
Blocked:被阻塞状态,一个线程试图获取一个内部对象锁(不是java.util.concurrent库中的锁),而该锁被其他线程持有,则该线程进入阻塞状态,当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程将变成非阻塞状态
Waiting:等待状态,当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态(调用Object.wait()或Thread.join(),等待java.util.concurrent库中的Lock或Condition)
Timed waiting:计时等待状态,调用待超时参数的方法,就会导致线程进入计时等待状态,这一状态将一直保持到超时期满或接收到适当的通知。带有超时参数的方法有Thread.sleep()、Object.wait()、Thread.join()、Lock.tryLock()以及Condition.await()
Terminated:被终止状态,(1).因为run()正常退出而自然死亡。(2).因为一个没有捕获的异常终止run()而意外死亡
线程所具有状态及从一个状态到另一个状态的转换,当一个线程被阻塞或等待时(或终止时),另外一个线程被调度为运行状态,当一个线程被重新激活(例如,因为超时期满或成功获得一个锁),调度器检查它是否具有比当前运行线程更高的优先级,如果是这样的,调度器从当前运行线程中挑选一个,剥夺其运行权,选择一个新的线程运行
5.使用interrupt()终止线程
interrupt():向线程发送中断请求,线程的中断状态将设置为true,并不是真正停止线程,如果当前线程被一个sleep调用阻塞,则抛出InterruptedException
interrupted():测试当前线程是否被中断,注意,这是一个静态方法,执行后将当前线程的中断状态重置为false
isInterrupted():测试当前线程是否被终止,不像静态的中断方法,不改变线程的中断状态
package io.laokou.test.concurrent; /** * @author Kou Shenhai * @version 1.0 * @date 2022/4/17 0017 下午 6:41 */ public class InterruptThreadTest { public static void main(String[] args) { System.out.println("运行线程..."); MyInterruptThreadTest thread = new MyInterruptThreadTest(); thread.start(); thread.interrupt(); System.out.println("中断线程..."); } static class MyInterruptThreadTest extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { String threadName = getName(); System.out.println("i=" + i); if (isInterrupted()) { System.out.println("通过isInterrupted()检测到中断"); System.out.println("线程" + threadName + "执行第一个interrupted() > " + interrupted()); System.out.println("线程" + threadName + "执行第二个interrupted() > " + interrupted()); } try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("循环结束,线程结束..."); } } } /** * 运行线程... * 中断线程... * i=0 * 通过isInterrupted()检测到中断 * 线程Thread-0执行第一个interrupted() > true * 线程Thread-0执行第二个interrupted() > false * i=1 * i=2 * i=3 * i=4 * i=5 * i=6 * i=7 * i=8 * i=9 * 循环结束,线程结束... */
6.线程优先级
注:yield()导致当前执行线程处于让步状态,如果其他的可运行线程具有与此线程同样高的优先级,那么这些线程接下来会被调度,注意,这是一个静态方法
(1).每一个线程都有一个优先级,默认情况下,一个线程继承它的父线程优先级。使用setPriority()来提高或降低任何一个线程的优先级,可以将优先级设置为MIN_PRIORITY(Thread类定义为1),与MAX_PORIORITY(Thread类定义为10)之间的任何值,NORM_PRIORITY被定义为5 当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程,但是,线程优先级是高度依赖于系统的。
(2).有几个高优先级的线程没有进入非活动状态,低优先级的线程可能永远也不能执行。每当调度器决定运行一个新线程时,首先会在具有高优先级的线程中选择,尽管这样会使低优先级的线程完全饿死。
7.守护线程
注:守护线程应该永远不去访问固有资源,因为它会在任何时候深圳在一个操作的中间发送中断
调用this.setDaemon(true)(这方法必须在线程启动之前调用)将线程转换为守护线程,守护线程唯一用途就是为其他线程提供服务(例如计时线程,它定时发送信号给其他线程或清空过时的高速缓存项的线程),当只剩下守护线程时,虚拟机就退出了(当只剩下守护线程,就没必要继续运行程序)
8.同步
在大多数实际的多线程应用中,两个或两个以上的线程需要共享对同一个数据的存取
执行 account[to] += amount指令,因为不是原子性,该指令可能被处理如下:
(1).将account[to]加载到寄存器
(2).增加account
(3).将结果写出account[to]
线程1执行步骤1和步骤2,然后被操作系统剥夺运行权,假设线程2被唤醒并修改account[to]的值,然后线程1被唤醒并完成步骤3
package io.laokou.test.concurrent; import java.util.Arrays; /** * @author Kou Shenhai * @version 1.0 * @date 2022/4/19 0019 上午 8:30 */ public class UnSyncBankTest { private static final int NACCOUNTS = 100; private static final double INITIAL_BALANCE = 1000; private static final double MAX_AMOUNT = 1000; private static final int DELAY = 10; public static void main(String[] args) { Bank bank = new Bank(NACCOUNTS,INITIAL_BALANCE); for (int i = 0; i < NACCOUNTS; i++) { int fromAccount = i; Runnable runnable = () -> { try { while (true) { int toAccount = (int)(bank.size() * Math.random()); double amount = MAX_AMOUNT * Math.random(); bank.transfer(fromAccount,toAccount, amount); Thread.sleep((int)(DELAY * Math.random())); } } catch (Exception e) {} }; new Thread(runnable).start(); } } } class Bank { private final double[] accounts; public Bank(int n,double initialBalance) { this.accounts = new double[n]; Arrays.fill(accounts, initialBalance); } public void transfer(int from,int to,double amount) { if (accounts[from] < amount) { return; } System.out.print(Thread.currentThread()); accounts[from] -= amount; System.out.printf("%10.2f from %d to %d",amount,from,to); accounts[to] += amount; System.out.printf(" Total Balance: %10.2f%n",getTotalBalance()); } public double getTotalBalance() { double sum = 0; for (int i = 0; i < accounts.length; i++) { sum += accounts[i]; } return sum; } public int size() { return accounts.length; } } /** * Thread[Thread-52,5,main] 407.97 from 52 to 23 Total Balance: 99964.67 * Thread[Thread-38,5,main] 80.38 from 38 to 43 Total Balance: 99964.67 * Thread[Thread-67,5,main] 326.22 from 67 to 23 Total Balance: 99964.67 * Thread[Thread-59,5,main] 209.93 from 59 to 40 Total Balance: 99964.67 * Thread[Thread-60,5,main] 27.81 from 60 to 78 Total Balance: 99964.67 * Thread[Thread-74,5,main] 186.07 from 74 to 56 Total Balance: 99964.67 * Thread[Thread-82,5,main] 428.53 from 82 to 70 Total Balance: 99964.67 * Thread[Thread-34,5,main] 414.20 from 34 to 42 Total Balance: 99964.67 * Thread[Thread-23,5,main] 122.91 from 23 to 85 Total Balance: 99964.67 * Thread[Thread-23,5,main] 116.56 from 23 to 83 Total Balance: 99964.67 * Thread[Thread-69,5,main] 346.14 from 69 to 55 Total Balance: 99964.67 * Thread[Thread-90,5,main] 504.83 from 90 to 10 Total Balance: 99964.67 * Thread[Thread-85,5,main] 157.97 from 85 to 41 Total Balance: 100000.00 */