并发编程学习笔记

1、同步与异步
同步:所有的操作都做完,才返回给用户。
异步:不用等所有操作等做完,就相应用户请求。
2、并发与并行
并行:是指两个或多个事件在同一时刻发生。
并发:是指两个或多个事件在同一时间段内发生。
3、临界区
用来表示一种公共资源或是共享数据,同时被多个线程式样,但是每一次只能有一个线程使用,一旦临界区资源被占用,其他线程想要使用这个资源就必须等待。
4、阻塞与非阻塞
阻塞:一个线程占用了临界区资源,那么其它所有需要这个资源的线程就必须在这个临界区中进行等待,导致线程挂起。此时如果占用资源线程不愿意释放资源,那么其它所以阻塞在这个临界区上的线程都不能工作。
非阻塞:允许多个线程同时进去临界区。
5、死锁
是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
死锁发生的四个条件
1)互斥条件:线程对资源的访问是排他性的,如果一个线程对占用了某资源,那么其他线程必须处于等待状态,直到资源被释放。
2)请求和保持条件:线程T1至少已经保持了一个资源R1占用,但又提出对另一个资源R2请求,而此时,资源R2被其他线程T2占用,于是该线程T1也必须等待,但又对自己保持的资源R1不释放。
3)不剥夺条件:线程已获得的资源,在未使用完之前,不能被其他线程剥夺,只能在使用完以后由自己释放。
4)环路等待条件:在死锁发生时,必然存在一个“进程-资源环形链”,即:{p0,p1,p2,…pn},进程p0(或线程)等待p1占用的资源,p1等待p2占用的资源,pn等待p0占用的资源。(最直观的理解是,p0等待p1占用的资源,而p1而在等待p0占用的资源,于是两个进程就相互等待)
6、活锁
指线程1可以使用资源,但它很礼貌,让其他线程先使用资源,线程2也可以使用资源,但它很绅士,也让其他线程先使用资源。这样你让我,我让你,最后两个线程都无法使用资源。
7、饥饿
指如果线程T1占用了资源R,线程T2又请求封锁R,于是T2等待。T3也请求资源R,当T1释放了R上的封锁后,系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁R,当T3释放了R上的封锁之后,系统又批准了T4的请求……,T2可能永远等待。
某一个或多个线程因为种种原因无法获得所需的资源,导致一直无法执行。
8、并发级别
阻塞、无障碍、无锁、无等待
无障碍:一种最弱的非阻塞调度,线程自由进入临界区。无竞争时,有限步内完成操作;有竞争时,回滚数据。
无锁:是无障碍的(无锁一定是无障碍的),在竞争时保证有一个线程可以胜出。
无等待:是无锁的,要求所有的线程都必须在有限步内完成,并且一定是无饥饿的。
9、线程与进程
线程:是进程内的执行单元。
进程:指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体。
10、原子性
一个操作是不可中断的。即使是多个线程一起执行时,一个操作一旦开始就不会被其它线程干扰。(i++不具备原子性)
11、有序性
在并发时,程序的执行可能出现乱序。
一条指令的执行是可以分为很多步骤的:
取指IF、译码和取寄存器操作数ID、执行或者有效地址计算EX、存储器访问MEM、写回WB
12、可见性
一个线程修改了某一个共享的值,其他线程是否能够立即知道这个修改。
13、happens-before原则规则
1)程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
2)锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作;
3)volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
4)传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
5)线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
6)线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
7)线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
8)对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;
14、线程安全
指某个函数、函数库在多线程环境中被调用时,能够正确地处理各个线程的局部变量,使程序功能正确完成。

java中保证i++线程安全:

public class AccountingSync implements Runnable{
    static AccountingSync instance=new AccountingSync();
    static int i=0;
    @Override
    public void run() {
        for(int j=0;j<10000000;j++){
            synchronized(instance){
                i++;
            }
        }
    }
}

15、新建线程:
1)继承Thread类创建线程

public class MyThread extends Thread{
  public void run(){
  //重写run方法
  }
}
public class Main {
  public static void main(String[] args){
    new MyThread().start();//创建并启动线程
  }
}

2)实现Runnable接口创建线程

public class MyThread2 implements Runnable {
  public void run(){
  //重写run方法
  }
}
public class Main {
  public static void main(String[] args){
    //创建并启动线程
    MyThread2 myThread=new MyThread2();
    Thread thread=new Thread(myThread);
    thread().start();
    //或者    new Thread(new MyThread2()).start();
  }
}

3)使用Callable和Future创建线程

public class Main {
  public static void main(String[] args){
   MyThread3 th=new MyThread3();
   //使用Lambda表达式创建Callable对象
   //使用FutureTask类来包装Callable对象
   FutureTask<Integer> future=new FutureTask<Integer>(
      (Callable<Integer>)()->{
         return 5;
      }
    );
   new Thread(task,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程
    try{
    System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回
    }catch(Exception e){
    ex.printStackTrace();
   }
  }
}

16、中断线程
public void Thread.interrupt() // 中断线程
public boolean Thread.isInterrupted() // 判断是否被中断
public static boolean Thread.interrupted() // 判断是否被中断,并清除当前中断状态

public static native void sleep(long millis) throws InterruptedException{
    public void run(){
    while(true){
        if(Thread.currentThread().isInterrupted()){
            System.out.println("Interruted!");
            break;
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            System.out.println("Interruted When Sleep");
            //设置中断状态,抛出异常后会清除中断标记位
            Thread.currentThread().interrupt();
        }
        Thread.yield();
    }
}

17、挂起(suspend)和继续执行(resume)线程
– suspend()不会释放锁
– 如果加锁发生在resume()之前 ,则死锁发生
18、等待线程结束(join)和谦让(yeild)

public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException

19、守护线程

Thread t=new DaemonT();
t.setDaemon(true);
t.start();

20、线程优先级

public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

Thread high=new HightPriority();
LowPriority low=new LowPriority();
high.setPriority(Thread.MAX_PRIORITY);
low.setPriority(Thread.MIN_PRIORITY);
low.start();
high.start();

21、基本的线程同步操作
1)synchronized
– 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
– 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
– 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。

1.1)指定加锁对象

public void run() {
    for(int j=0;j<10000000;j++){
        synchronized(instance){
            i++;
        }
    }
}

1.2)用在方法上

public synchronized void increase(){
    i++;
}
public static synchronized void increase(){
    i++;
}

2)Object.wait() Obejct.notify() 会释放锁
2.1)

public static class T1 extends Thread{
    public void run(){
        synchronized (object) {
            System.out.println(System.currentTimeMillis()+":T1 start! ");
            try {
                System.out.println(System.currentTimeMillis()+":T1 wait for object ");
                object.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis()+":T1 end!");
        }
    }
}

2.2)

public static class T2 extends Thread{
    public void run(){
        synchronized (object) {
            System.out.println(System.currentTimeMillis()+":T2 start! notify one thread");
            object.notify();
            System.out.println(System.currentTimeMillis()+":T2 end!");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
        }
    }
}

22、CAS算法
CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的变量(内存值),E表示预期值(旧的),N表示新值。当且仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。CAS操作是抱着乐观的态度进行的(乐观锁),它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即时没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
23、无锁类
JDK1.5的原子包:java.util.concurrent.atomic
1)AtomicInteger
2)Unsafe
3)AtomicReference
4)AtomicStampedReference
5)AtomicIntegerArray
6)AtomicIntegerFieldUpdater
24、JDK并发包
1)ReentrantLock:可重入/可中断(lockInterruptibly())/可限时/公平锁(先来先得)
2)Condition:类似于 Object.wait()和Object.notify(),与ReentrantLock结合使用
3)Semaphore:运行多个线程同时临界区
4)ReadWriteLock:ReadWriteLock是JDK5中提供的读写分离锁
读-读不互斥:读读之间不阻塞。
读-写互斥:读阻塞写,写也会阻塞读。
写-写互斥:写写阻塞。
5)CountDownLatch:倒数计时器
6)CyclicBarrier:Cyclic意为循环,也就是说这个计数器可以反复使用。
7)LockSupport:提供线程阻塞原语
8)ReentrantLock
25、线程池的种类
newFixedThreadPool
newSingleThreadExecutor
newCachedThreadPool
newScheduledThreadPool

猜你喜欢

转载自blog.csdn.net/xiaoshiyiqie/article/details/79666947