Java并发总结


进程与线程的区别

进程是程序的运行过程,引入进程可以使得多个程序并发执行。一个进程下可以包含多个进程,多个线程彼此之间独立,但是共享进程的资源。

同步与异步
  • 同步是指按照顺序处理资源,当资源被占有时,后续操作需要等待占有结束才能继续运行。
  • 异步是指当A程序正在占用资源时,而B也需要对资源进行处理,发送通知给A后可以继续进行其他的操作,当A运行结束时,B再运行。
阻塞与非阻塞
  • 阻塞指的是资源被占有时,不得不停下来等待完成,才能完成后续操作,等待的过程即为阻塞状态
  • 非阻塞是指资源被占有时,不需要等到资源占用结束,另外的进程得到正在占有的结果后就可以继续其他的操作了。
多线程实现方式
  • 继承Thread类,新建类调用start方法完成线程的启动。
  • 实现Runnable接口,启动类中调用Thread的构造方法,调用start方法完成启动。
  • 实现Callable接口,与Runnable的区别在于线程启动可以有返回值的功能,利用Future类接收。
多线程状态
  • 新建状态,类创建成功,实例存在虚拟机的堆中,完成内存的分配。
  • 就绪状态,调用start方法后,进入就绪状态。
  • 运行状态,当时间片轮到该线程,则开始运行,进入运行状态。
  • 阻塞状态,线程调用类相关方法如sleep,wait等,线程进入阻塞状态。
  • 结束状态,线程调用stop方法,或者线程本身运行结束,进入结束状态。
线程常用方法
  • sleep,线程处于阻塞状态,锁不释放
  • wait,线程处于阻塞状态,锁释放
  • yield,线程由运行状态转化为就绪状态,如果时间片合适,立即转化为运行状态
线程通信方式
wait()/notify()
  • wait()方法: 让当前线程进入等待,并释放锁。
  • notify()方法: 让当前线程通知那些处于等待状态的线程,当前线程执行完毕后释放锁,并从其他线程中唤醒其中一个继续执行。
  • notifyAll()方法: 让当前线程通知那些处于等待状态的线程,当前线程执行完毕后释放锁,将唤醒所有等待状态的线程。
Condition实现等待/通知

ReentrantLock也可以实现同样的功能,但需要借助于Condition对象。
- condition.await()
- condition.signal()
- condition.signaAll()

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


public class ThreadAB {
    public String str = new String("");
    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public Runnable threadA = new Runnable(){

        public void run() {
            // TODO Auto-generated method stub
            synchronized (str){
                try {
                    System.out.println("start wait");
                    str.wait();
                    System.out.println("end wait");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }

    };
    public Runnable threadB = new Runnable(){

        public void run() {
            // TODO Auto-generated method stub
            synchronized (str){
                System.out.println("start notify");
                str.notify();
                System.out.println("end notify");
            }

        }

    };
    public Runnable threadC = new Runnable(){

        public void run() {
            // TODO Auto-generated method stub
            lock.lock();
            try {
                System.out.println("start condition.await()");
                condition.await();
                System.out.println("end condition.await()");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            lock.unlock();

        }

    };
    public Runnable threadD = new Runnable(){

        public void run() {
            // TODO Auto-generated method stub
            lock.lock();
            System.out.println("start condition.signal()");
            condition.signal();
            System.out.println("end condition.signal()");
            lock.unlock();


        }

    };


}


public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ThreadAB th = new ThreadAB();
        Thread thA = new Thread(th.threadA);
        Thread thB = new Thread(th.threadB);
        thA.start();
        thB.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Thread thC = new Thread(th.threadC);
        Thread thD = new Thread(th.threadD);
        thC.start();
        thD.start();
    }

}
线程同步
  • Synchronized
    Synchronized可以修饰方法或代码块,无论修饰的是什么,都是对首先获取对象或方法的监视器,之后对其加锁,这样其他的线程就无法访问该对象或方法了,在线程运行结束后,释放锁,就可以达到线程同步的目的。
  • Lock
    Lock是Java提供给开发者的接口,可以显示地控制锁的获取和释放,Synchronized释放锁时不需要开发者控制,Lock可以控制释放锁的位置,更加方便释放。
重入锁

重入锁实现的是Lock接口,可以对一个对象多次上锁。

读写锁

读写锁可以保证只对文件的写操作加锁,而读操作可以并发执行,增大了读写效率

死锁

两个方法都需要获得A锁和B锁。一个线程执行a方法且已经获得了A锁,在等待B锁;另一个线程执行了b方法且已经获得了B锁,在等待A锁。这种状态,就是发生了静态的锁顺序死锁。

死锁产生的条件:

  • 互斥条件:一个资源每次只能被一个线程使用。
  • 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:线程已获得的资源,在未使用完之前,不能强行剥夺。
  • 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
乐观锁 悲观锁 可中断锁 公平锁
  • 悲观锁,当资源被其中一个线程占有时,另外一个线程需要等待结束才能运行,此时该线程需要进入阻塞状态,当进入阻塞状态后,资源被释放,浪费CPU资源。
  • 乐观锁,当资源被其中一个线程占用时,另外一个线程不是进入阻塞状态,而是采用自旋的方法等待资源占用结束。
  • 可中断锁,在线程执行的过程中,可以放弃后边未执行的流程,中断线程。如Synchronized是不可中断锁,Lock为可中断锁。
  • 公平锁,所有的线程具有相同的优先级,公平竞争资源,非公平锁即各个线程优先级不同,可能低优先级的线程永远得不得执行。
线程池

为了更方便的管理线程资源,减少线程重建销毁的次数,引入了线程池的概念,线程池可以管理多个线程,设置核心线程数量,总线程数量,当有任务需要处理时,优先使用核心线程处理,核心线程已满时,进入排队序列等待,当排队序列已满并且未达到最大线程数,则采用普通线程处理。如果已经达到最大线程数,返回异常结果。

线程池构造方法参数:
- 核心线程数量
- 最大线程数量
- 超时时长
- 阻塞队列
- 拒绝策略

四种线程池类
- newFixedThreadPool,该线程池是一种线程数量固定的线程池,在这个线程池中 所容纳最大的线程数就是我们设置的核心线程数。 如果线程池的线程处于空闲状态的话,它们并不会被回收,除非是这个线程池被关闭。如果所有的线程都处于活动状态的话,新任务就会处于等待状态,直到有线程空闲出来。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>());
}
  • newCachedThreadPool,核心线程数为0, 线程池的最大线程数Integer.MAX_VALUE。当线程池中的线程都处于活动状态的时候,线程池就会创建一个新的线程来处理任务。该线程池中的线程超时时长为60秒,所以当线程处于闲置状态超过60秒的时候便会被回收。
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
        60L, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>());
}
  • newScheduledThreadPool,它的核心线程数是固定的,对于非核心线程几乎可以说是没有限制的,并且当非核心线程处于限制状态的时候就会立即被回收。
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}
  • newSingleThreadExecutor,在这个线程池中只有一个核心线程,对于任务队列没有大小限制,也就意味着这一个任务处于活动状态时,其他任务都会在任务队列中排队等候依次执行。
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>()));
}
Java并发包
  • ConcurrentHashMap与HashMap的区别在于,每个桶都加上了锁,这样当加入数据时,先判断是哪个桶,之后对其加上锁,当其他线程再方位这个桶时进入阻塞状态,而如果访问其他的桶则没有影响。读操作则不需要对桶加锁,加快了访问速度。
Java内存模型

Java的内存中各个线程之间相互独立,每个线程都对应一个缓存空间,他们共享一块主内存,当其中一个线程修改共享变量时,首先修改缓存中的变量,之后将修改后的变量刷新到主内存。

Java并发中经常遇到的三个问题,原子性、顺序性、可见性。

volatile实现

volatile具有可见性和顺序性,但不具有原子性。

CAS

CAS操作包含三个操作数,内存位置(V)、预期原值(A)和新值(B)。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值