作者:liuliyu2000_867 | 来源:互联网 | 2023-09-14 08:39
多线程与并发的基础问题 并发就是指程序同时处理多个任务的能力(一个程序被多个用户访问都能看到自己预期的结果) 并发的根源在于对多任务情况下访问资源的有效控制!
public class DownloadSimple {private static int user = 1 ;private static int dowloadCounts = 5000 ;private static int count= 0 ;public static void main (String[] args) {ExecutorService executor = Executors.newCachedThreadPool();final Semaphore semaphore = new Semaphore(user);for (int i =0 ;i
user=1单个用户访问
user=10多个用户访问的时候:
无处不存在并发的问题: 并发控制是关键问题
程序、进程和线程的区别? 程序是静态的概念,windows下是指.exe程序 进程是动态的概念,是程序在运行状态,进程说明程序在内存中的边界 线程是指进程内的一个“基本任务”,每个线程都有自己的功能,是cpu分配与调度的基本单位。
一个程序有多个进程(之间互不影响)
多核CPU是线程并行的 单核CPU是线程并发的(CPU的时间片)
同步和异步:
回调(异步)–Ajax NIO-非阻塞IO
临界区:(eg:办公室中的一台打印机) 临界区表示一种公共资源与共享数据,可以被多个线程使用。 同一时间只能有一个线程访问临界区域(阻塞状态),其它资源必须等待。
避免以上锁发生的解决办法:为每一个线程分配一个自己独立的不被其他影响的资源
线程安全:在拥有共享数据的多个线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
线程安全不安全就是区别单用户多用户对公共访问资源的处理是否符合预期处理
线程安全的三大特性: 原子性: 即一个操作或者多个操作,要么全部执行且执行的过程不会被其他因素打断,要么就都不执行 i = i +1 可见性: 当多个线程访问同一个变量的时候,一个线程修改了这个变量的值,其它线程能够立即看得到修改的值
用户1修改之后,没有通知用户2线程
有序性: 如果在本线程内观察,所有的操作都是有序的,如果在一个线程内观察另外一个线程,所有操作都是无序的;
JVM自动进行防止重排序,所以应该不会出现重排序,打乱有序性
*Java内存模型 Java Memory Model(在真实物理内存上开辟的空间)
堆内存(Heap):用于存储对象,JVM全局唯一,堆是不连续的,执行效率低,所有线程共享 方法区(静态区):类结构信息,静态变量和静态方法信息,存储内容是不变的,存储字符串,在堆中开辟的一块空间
栈内存(Stack):每个线程创建一个栈,存储执行方法的执行信息,线程私有,无法共享。连续存储,执行效率高,先进后出,后进先出
直至main函数弹出栈 销毁线程栈,销毁线程
Java多线程 创建多线程的三种方式: 1.继承Thread类创建线程 2.实现Runnable接口来创建线程 3.使用Callable和Future创建线程 //使用继承Thread的方式来实现多线程
public class Methed1 {public static void main(String[] args) {new ExtendsThread().start();}}class ExtendsThread extends Thread {public void run(){Integer speed &#61; new Random().nextInt(100 );for (int i &#61; 0 ;i<100 ;i&#43;&#43;){try {Thread.sleep(1000 );} catch (InterruptedException e) {e.printStackTrace();}System.out.println(this.getName()&#43;"---已前进--" &#43;(i*speed)&#43;"---速度----" &#43;speed);}}}
三个并列的线程可能执行顺序不一样
ExtendsThread liuxiang &#61; new ExtendsThread();liuxiang.setName("刘翔" );ExtendsThread wanghaoxin &#61; new ExtendsThread();wanghaoxin.setName("王五" );ExtendsThread lisan &#61; new ExtendsThread();lisan.setName("路飞" );liuxiang.start();wanghaoxin.start();lisan.start();
执行顺序依赖于线程的优先级&#xff0c;可以手动设置
程序一旦程序启动之后有五个线程
java本身运行的过程中就是多线程的
垃圾回收线程和main主线程
在Runnable接口中无法获取this.getName()获取线程名称 只能用:
public Thread (Runnable target) {init(null , target, "Thread-" &#43; nextThreadNum(), 0 );}
通过实现Runnable接口来新建线程
public class Methed2 {public static void main(String[] args) {ImplRunnable liuxiang &#61; new ImplRunnable();Thread thread &#61; new Thread(liuxiang);thread.setName("liuxian" );Thread thread2 &#61; new Thread(new ImplRunnable());thread2.setName("WANGHAXIN" );thread.start();thread2.start();}}class ImplRunnable implements Runnable { &#64;Overridepublic void run() {Integer speed &#61; new Random().nextInt(100 );for (int i &#61; 0 ;i<100 ;i&#43;&#43;){try {Thread.sleep(1000 );} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()&#43;"---已前进--" &#43;(i*speed)&#43;"---速度----" &#43;speed);}}}
实现Callable接口–利用线程池 jdk1.5之后为我们专门提供了一个并发工具包&#xff0c;java.util.concurrent。 java.util.concurrent包含许多线程安全、测试良好、高性能的并发构建块。创建concurrent的目的就是要实现Collection框架对数据结构所执行的并发操作。通过提供一组可靠的&#xff0c;高性能并发构建块&#xff0c;开发人员可以提高并发类的线程安全、可伸缩性、性能、可读性和可靠性
Callable< Integer > myCallabel&#61; new implCall(); FutureTask< Integer > ft &#61; new FutureTask< Integer > (myCallabel); System. out. println(Thread . currentThread(). getName()&#43; " " &#43; i);Thread th&#61; new Thread (ft );th. start();
专门用于并发机制
需要线程的数目已经在创建线程池时已经定义好 liuxiang是实现Callable接口的实例化对象&#xff0c;Future用于接收返回值
implCall liuxiang &#61; new implCall();Future future &#61; executorService.submit(liuxiang);
代码案例&#xff1a;&#xff08;线程池实现&#xff09;
public class Methed3 {public static void main (String[] args) {ExecutorService executorService &#61; Executors.newFixedThreadPool(3 );implCall liuxiang &#61; new implCall();implCall wanghaoxin &#61; new implCall();implCall lufei &#61; new implCall();Future future &#61; executorService.submit(liuxiang);Future future2 &#61; executorService.submit(wanghaoxin);Future future3 &#61; executorService.submit(lufei);try {Thread.sleep(3000 );} catch (InterruptedException e) {e.printStackTrace();}finally {executorService.shutdown();}try {System.out.println("累计跑了" &#43; future.get());System.out.println("累计跑了" &#43; future2.get());System.out.println("累计跑了" &#43; future3.get());} catch (Exception e) {e.printStackTrace();}}}/*** 实现Callable接口可以允许我们的线程返回值或者抛出异常* &#64;author Administrator**/ class implCall implements Callable{&#64;Override public Object call () throws Exception {Integer speed &#61; new Random().nextInt(100 );Integer distince &#61; 0 ; for (int i &#61; 0 ;i<100 ;i&#43;&#43;){distince &#61; i*speed;System.out.println(Thread.currentThread().getName()&#43;"---已前进--" &#43;(i*speed)&#43;"---速度----" &#43;speed);}return distince;}}
这三个线程是彼此没有任何交集&#xff0c;利用线程池手段是开发的主要实现方案–方便对线程池进行有效管理&#xff08;最著名–数据库连接池&#xff0c;数据库中的每一个连接其实就是一个线程&#xff0c;线程池就是连接池的底层实现&#xff09;
线程池更安全—-保证线程安全的工具类
1&#xff09;继承Thread&#xff1a;优点&#xff1a;编程简单&#xff0c;执行效率高&#xff0c;但继承&#xff0c;无法对线程组进行有效控制&#xff0c;任何使用场景都不推荐 2&#xff09;实现Runnable接口&#xff0c;面向接口编程&#xff0c;执行效率高&#xff0c;无法对线程组进行有效控制&#xff0c;没有返回值。异常&#xff0c;使用场景&#xff1a;适用于简单的多线程程序 3&#xff09;利用线程池&#xff1a;容器管理线程&#xff0c;允许返回值和异常&#xff0c;执行效率相对低&#xff08;容器本身有调度的时间耗费&#xff0c;线程切换等等&#xff09;&#xff0c;编程麻烦 &#xff0c;使用场景&#xff1a;企业级应用&#43;推荐使用。