Java线程并发基础

1、线程的生命周期
这里写图片描述
1.1、线程生命周期的5种状态
(1)创建(new):创建Thread类实例,已占用内存,new Thread()。
(2)就绪(runnable):线程已经被启动,正在等待被分配给CPU时间片,start()。
(3)运行(running):线程获取CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
(4)阻塞(blocked):由于某种原因导致正在运行的线程让出CPU并暂停自己yi的执行,即进入阻塞状态。
①正在睡眠:用sleep(long t)方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
②正在等待:调用wait()方法。可调用notify()方法回到就绪状态。
③被另一个线程所阻塞:调用suspend()方法。可调用resume()方法恢复。
(5)死亡(dead):当线程执行完毕或被其他线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
1.2、线程常用方法
(1)void run():创建该类的子类时必须实现的方法
(2)void start():开启线程的方法
(3)static void sleep(long t)/static void sleep(long millis, int nanos):释放CPU的执行权,不释放锁。当前线程睡眠millis的时间(millis指定睡眠时间是其最小的不执行时间,因为sleep(millis)休眠到达后,无法保证会被JVM立即调度),sleep()是一个静态方法,所以他不会停止其他的线程也处于休眠状态。线程sleep()时不会失去拥有的对象锁。作用是:保持对象锁,让出CPU,调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留一定的时间给其他线程执行的机会。
(4)final void wait():释放CPU的执行权,释放锁。当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去了对象的锁——暂时的,wait后还要返还对象锁。当前线程必须用用当前对象的锁,如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常,所以wait()必须在synchronized block中调用。
(5)final void notify()/notifyAll():唤醒在当前对象等待池中等待的第一个线程/所有线程。notify()/notifyAll()也必须拥有相同对象锁,否则也会抛出IllegalMonitorStateException异常。
(6)static void yied():可以对当前线程进行临时暂停,让出CPU的使用权,给其他线程执行机会,让同等优先级的线程运行(但并不保证当前线程会被JVM再次调度、使该线程重新进入Running状态),如果没有同等优先级的线程,那么yied()方法将不会骑作用。
(7)final void join():等待该线程终止。这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
(8)interrupt():http://blog.csdn.net/axman/article/details/562249
2、守护进程
守护进程,可以简单地理解为后台运行线程。进程结束,守护线程自然而然地会结束,不惜要手动的去关心和通知其状态。
调用线程对象的方法setDaemon(true),则可以将其设置为守护线程,该方法必须在启动线程前调用。
3、ThreadLocal
当使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。从线程的角度看,目标变量就像是线程的本地变量,这也是类名中Local所要表达的意思。
4、synchronized
synchronized是Java语言的关键字,当它用于修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该代码。其实简单一点理解,就是要解决多线程并发时候的“时序性问题”,即访问要有一个顺序,讲究先来后到,就看谁先能拿到这个锁对象。
5、Lock
Lock是一个接口提供了无条件的、可轮训的、定时的、可中断的锁获取操作,所有加锁和解锁的方式都是显式的。
包路径:java.util.concurrent.locks.Lock
核心方法:lock()、unlock()、tryLock()
实现类:ReentrantLock、ReentrantReadWriteLock、StampedLock
6、synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock简单对比
(1)synchronized是在JVM层面上实现的,可以通过一些监控工具监控synchronized的锁定,当代码执行时出现异常,JVM会自动释放锁定。当只有少量竞争者的时候,synchronized是一个很好的通用的锁实现。synchronized的锁是针对一个对象的。
(2)ReentrantLock、ReentrantReadWriteLock、StampedLock都是代码 块层面的锁定,要保证锁定一定会被释放,就必须将unlock()放到finally{}中。
(3)ReentrantLock是一个很好的通用的锁实现,使用于比较简单的加锁、解锁的业务逻辑,如果实现复杂的锁机制,当线程增长能够预估时也是可以的。
(4)ReentrantReadWriteLock对Lock又进行了扩展,引入了read和write阻塞和并发机制,相对于ReentrantLock,它可以实现更复杂的锁机制,且并发性也更高些。
(5)StampedLock又在Lock的基础上,实现了可以满足乐观锁和悲观锁等一些在读线程越来越多的业务场景,对吞吐量有巨大的改进,但并不是说要替代之前的Lock,毕竟它还是有些应用场景的。
(6)StampedLock有一个复杂的API相对于前面两种Lock锁,对于加锁操作,很容易误用其他方法,如果理解不深入也更容易出现死锁和不必要的麻烦。
(7)推荐如果不是业务非得需要,建议使用ReentrantLock、ReentrantReadWriteLock即可满足大部分业务场景的需要。
7、死锁
在两段不同的逻辑都在等待对方的锁释放才能继续往下工作时,这个时候就会产生死锁,表面现象就是程序再也执行不下去了。
8、volatile
每次读取volatile的变量时都要从它的内存地址中读取,并没有说每次修改完volatile的变量后都要立刻将它的值写回内存。即高并发的安全机制是不可靠的。
加锁机制既可以确保可见性又可以确保原子性,而volatile变量只有确保可见性。
9、atomic
atomic是不会阻塞线程(或者说智能在硬件级别上阻塞了),线程安全的加强版的volatile原子操作。
包路径:java.util.concurrent.atomic
10、线程安全、高并发性能的单例模式
第一种:

class Singleton{
    private static Singleton instance;
    private static byte[] lock = new byte[0];
    private Singleton{}
    public static sybchronized Singleton getInstance(){
        if(instance == null){
            synchronized(lock){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

第二种:

class Singleton{
    private static Singleton instance;
    private static ReentrantLock lock = new ReentrantLock();
    private Singleton{}
    public static sybchronized Singleton getInstance(){
        if(instance == null){
            lock.lock();
            if(instance == null){
                instance = new Singleton();
            }
            lock.unlock();
        }
        return instance;
    }
}

11、线程安全的集合类
java.util.Hashtable
java.util.concurrent.ConcurrentHashMap
java.util.concurrent.CopyOnWriteArrayList
java.util.concurrent.CopyOnWriteArraySet
Vector
12、CopyOnWrite机制介绍
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是可以对CopyOnWrite容器进行并发地读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
13、StringBuffer与StringBuilder
在编写JAVA代码的过程中有时要频繁地对字符串进行拼接,如果直接用“+”拼接的话会建立很多的String型对象,严重的话会对服务器资源和性能造成不小的影响;而使用StringBuilder和StringBuffer能解决以上问题。而StringBuffer是线程安全的,而StringBuilder不是线程安全的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值