Java并发多线程进阶-1
来源:互联网 发布:五大常任理事国 知乎 编辑:程序博客网 时间:2024/05/02 09:54
1.synchronized和lock
1.1synchronized
synchronized( 需要一个任意的对象){//代码块中放操作共享数据的代码。}synchronized的缺陷:
synchronized是java中的一个关键字,也就是说是Java语言内置的特性。如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
2)线程执行发生异常,此时JVM会让线程自动释放锁。
1.2 lock
lock()、tryLock()、tryLock(long time, TimeUnit unit)、lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。四个获取锁方法的区别:
lock()方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待。
由于在前面讲到如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。
tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的。
ReentrantLock:直接使用lock接口的话,我们需要实现很多方法,不太方便,ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法,ReentrantLock,意思是“可重入锁”。
ReentrantReadWriteLock:实现了ReadWriteLock接口,一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。ReentrantReadWriteLock里面提供了很多丰富的方法,不过最主要的有两个方法:readLock()和writeLock()用来获取读锁和写锁。
1.3lock和synchronized的区别
1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
2.线程池
了强力的支持。线程池的5中创建方式:
1、Single Thread Executor : 只有一个线程的线程池,因此所有提交的任务是顺序执行,
2、Cached Thread Pool : 线程池里有很多线程需要同时执行,老的可用线程将被新的任务触发重新执行,如果线程超过60秒内没执行,那么将被终止并从池中删除,
3、Fixed Thread Pool : 拥有固定线程数的线程池,如果没有任务执行,那么线程会一直等待。在构造函数中的参数4是线程池的大小,你可以随意设置,也可以和cpu的核数量保持一致,获取cpu的核数量int cpuNums = Runtime.getRuntime().availableProcessors();
4、Scheduled Thread Pool : 用来调度即将执行的任务的线程池,可能是不是直接执行, 每隔多久执行一次策略型的
5、Single Thread Scheduled Pool : 只有一个线程,用来调度任务在指定时间执行
2.1线程池的使用
调用excute,提交任务, 匿名Runable重写run方法, run方法里是业务逻辑
public class ThreadPoolWithRunable { public static void main(String[] args) { //创建一个线程池 ExecutorService pool = Executors.newCachedThreadPool(); for(int i = 1; i < 5; i++){ pool.execute(new Runnable() { @Override public void run() { System.out.println("thread name: " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } pool.shutdown(); }}提交 Callable,该方法返回一个 Future 实例表示任务的状态
调用submit提交任务, 匿名Callable,重写call方法, 有返回值, 获取返回值会阻塞,一直要等到线程任务返回结果
public class ThreadPoolWithcallable { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService pool = Executors.newFixedThreadPool(4); for(int i = 0; i < 10; i++){ Future<String> submit = pool.submit(new Callable<String>(){ @Override public String call() throws Exception { //System.out.println("a"); Thread.sleep(5000); return "b--"+Thread.currentThread().getName(); } }); //从Future中get结果,这个方法是会被阻塞的,一直要等到线程任务返回结果 System.out.println(submit.get()); } pool.shutdown(); }}
3.BlockingQueue
插入:
1)add(anObject):把anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则抛出异常,不好
2)offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false.
3)put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续, 有阻塞, 放不进去就等待
读取:
4)poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null; 取不到返回null
5)take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止; 阻塞, 取不到就一直等其他
int remainingCapacity();返回队列剩余的容量,在队列插入和获取的时候,不要瞎搞,数据可能不准, 不能保证数据的准确性
boolean remove(Object o); 从队列移除元素,如果存在,即移除一个或者更多,队列改变了返回true
public boolean contains(Object o); 查看队列是否存在这个元素,存在返回true
int drainTo(Collection<? super E> c); //移除此队列中所有可用的元素,并将它们添加到给定 collection 中。取出放到集合中
int drainTo(Collection<? super E> c, int maxElements); 和上面方法的区别在于,指定了移动的数量; 取出指定个数放到集合
BlockingQueue有四个具体的实现类,常用的两种实现类为:
1、ArrayBlockingQueue:一个由数组支持的有界阻塞队列,规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的。
2、LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的。 LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。
LinkedBlockingQueue和ArrayBlockingQueue区别:
LinkedBlockingQueue和ArrayBlockingQueue比较起来,它们背后所用的数据结构不一样,导致LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于ArrayBlockingQueue.
- Java并发多线程进阶-1
- Java进阶篇:多线程并发实践
- JAVA进阶-多线程(1)
- Java进阶05 多线程
- JAVA进阶-多线程(2)
- JAVA进阶-多线程(3)
- JAVA进阶-多线程(4)
- java 多线程进阶
- Java进阶05 多线程
- Java进阶05 多线程
- Java进阶05 多线程
- Java进阶05 多线程
- java多线程进阶
- 掌握java多线程进阶
- Java进阶05 多线程
- Java并发系列-1、Java并发性和多线程介绍
- Android进阶系列11-Android多线程并发
- Java 并发和多线程向导(1)
- 计划预算(PV)、实际完成工作预算(EV)、实际成本(AC)
- process对象方法的介绍,Process.argv的使用
- 《Spring Security教程系列》三.HttpSecurity的使用
- 不隐藏已知文件类型的扩展名,黑客强制文件后缀名,影藏禁用注册表编译器
- sql高级函数汇总
- Java并发多线程进阶-1
- java 逆序打印链表的值
- H5的canvas属性
- Android RadioGroup设置选中,出现两个选中项(参考)
- POJ-3041-Asteroids
- iOS开发屏幕及其他视图层次
- nginx回顾
- Mybatis完全使用指南
- 得分SCORE UVA1585