串行和并行:
串行:一个线程在处理操作
并行:多个线程在处理同一个操作
什么叫做并发编程:
在多线程环境下,应用程序的执行
并发编程的目的:充分运用到资源,提高程序的效率
什么情况下用到并发编程:
1.在线程阻塞时,导致应用程序停止
2.处理任务时间过长时,可以创建子任务,来进行分段处理
3.间断任务执行
一.并发编程中待解决的问题
1.并发编程中频繁上下文切换的问题
频繁上下文切换,可能会带来一定的性能开销
2.如何减少上下文性能开销:
2.1.无锁并发编程
2.2.CAS
2.3.使用最少线程数量
2.4.协程:在单线程环境下进行多任务的调度,可以在多任务之间进行任务切换
3.并发编程中死锁问题
3.1什么是死锁
死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
例如,在某一个计算机系统中只有一台打印机和一台输入 设备,进程P1正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着的输入设备
这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。
例如下面的案例
package com.wish; public class SiSuo { //资源 private static final Object HAIR_A=new Object(); private static final Object HAIR_B=new Object(); public static void main(String[] args) { //进程P1 new Thread(()->{ //我在使用输入设备 synchronized (HAIR_A){ System.out.println("我在使用输入设备,提出使用打印机的请求"); //延迟时间 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //提出使用打印机的请求 synchronized (HAIR_B){ System.out.println("进程P1使用打印机"); } } }).start(); //进程P2 new Thread(()->{ //我在使用打印机 synchronized (HAIR_B){ System.out.println("我在使用打印机,提出使用输入设备的请求"); //延迟时间 try { Thread.sleep(100); //当前线程休眠,让渡CPU资源 } catch (InterruptedException e) { e.printStackTrace(); } //提出使用输入设备的请求 synchronized (HAIR_A){ System.out.println("进程P2使用输入设备"); } } }).start(); } }
结果:就是双方互相占有进程没有停止一直耗着
3.2死锁产生的原因
3.2.1.系统资源的竞争导致系统资源不足,以及资源分配不当,导致死锁。
3.2.2.进程在运行过程中,请求和释放资源的顺序不当,会导致死锁。
3.3死锁的四个必要条件
互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
循环等待条件: 若干进程间形成首尾相接循环等待资源的关系
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
3.4如何预防死锁问题
3.4.1.破坏请求和保持条件:在申请资源时,一次性将资源都申请到
3.4.2.破坏不可占用条件:抢占资源如何不满足,那就释放所有资源,以后如果再需要则再次申请即可
3.4.3.破坏循环等待条件
4.线程安全问题
多个线程同时操作同一个资源,可能会造成资源数据不安全问题
列如如下代码
package com.wish; import java.util.concurrent.CountDownLatch; public class ThreadTest { //资源 private static int num=0; //计算线程数量 private static CountDownLatch countDownLatch=new CountDownLatch(10); //对资源进行操作 public static void inCreate(){ num++; } public static void main(String[] args) throws InterruptedException { for (int i = 0 ; i < 10 ; i++ ){ new Thread(()->{ for (int j = 0 ; j < 100; j++){ inCreate(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } //每一个线程执行完毕,让计数-1 countDownLatch.countDown(); }).start(); } //等待计数器为0或者小于0执行await下面代码 countDownLatch.await(); System.out.println(num); } }
结果:它结果应该是1000但是它的结果却是681,就是应有许多个线程对它进行操作,但是互相都不知道别的线程的计算情况
解决线程不安全问题:
1.ReentrantLock对‘对资源的操作这一步骤’上一把锁,在执行完后在把锁释放掉
package com.wish; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.ReentrantLock; public class ThreadTest { //资源 private static int num=0; //计算线程数量 private static CountDownLatch countDownLatch=new CountDownLatch(10); private static ReentrantLock reentrantLock = new ReentrantLock(); //对资源进行操作 public static void inCreate(){ //上锁 reentrantLock.lock(); num++; reentrantLock.unlock(); } public static void main(String[] args) throws InterruptedException { for (int i = 0 ; i < 10 ; i++ ){ new Thread(()->{ for (int j = 0 ; j < 100; j++){ inCreate(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } //每一个线程执行完毕,让计数-1 countDownLatch.countDown(); }).start(); } //等待计数器为0或者小于0执行await下面代码 countDownLatch.await(); System.out.println(num); } }
2.使用synchronized关键字,对操作资源的方法进行修饰
package com.wish; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.ReentrantLock; public class ThreadTest { //资源 private static int num=0; //计算线程数量 private static CountDownLatch countDownLatch=new CountDownLatch(10); private static ReentrantLock reentrantLock = new ReentrantLock(); //对资源进行操作 public static synchronized void inCreate(){ //上锁 num++; } public static void main(String[] args) throws InterruptedException { for (int i = 0 ; i < 10 ; i++ ){ new Thread(()->{ for (int j = 0 ; j < 100; j++){ inCreate(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } //每一个线程执行完毕,让计数-1 countDownLatch.countDown(); }).start(); } //等待计数器为0或者小于0执行await下面代码 countDownLatch.await(); System.out.println(num); } }