串行和并行:

串行:一个线程在处理操作

并行:多个线程在处理同一个操作


什么叫做并发编程:

在多线程环境下,应用程序的执行

并发编程的目的:充分运用到资源,提高程序的效率

什么情况下用到并发编程:

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);
    }
}

  

 

posted on 2020-03-18 20:27  wishsaber  阅读(374)  评论(0编辑  收藏  举报