java并发基本总结
一、多线程的基本概念
-
什么是线程,跟进程什么区别?
一个程序可以同时执行多个任务,每个任务即为一个线程(Thread)。同时运行一个以上线程的程序称为多线程程序(multithreaded)。
多线程与多进程的本质区别在于,每个进程拥有自己的一整套变量,而线程则共享数据。与进程相比线程更轻量级。 -
创建线程的方式
- 实现Runnable接口实现run方法。
- extends Thread抽象类重写run方法。
创建多线程的demo
不要调用Thread类或Runnable对象的run方法。直接调用run方法只会执行同一个线程中的任务,而不会启动新线程。应该调用Thread.start方法。
- 使用线程池(待补充)
-
中断线程
-
当对一个线程调用interrupt方法时,线程的中断状态将被置位(这是每一个线程都具有的boolean标志。)每个线程都应该时不时的检查这个标志,以判断线程是否被置位。判断代码如下:
while(!Thread.currentThread().isInterrupted && more work to do){ do more work; }
-
**注意:**如果线程被阻塞(调用sleep或wait方法),就无法检测中断状态。这是产生InterruptedException异常的地方。
-
-
线程状态
-
New (新创建)
-
Runnable(可运行) 一旦调用start方法
-
Waitting (被阻塞)
i. 临界资源被其它资源所持有,该线程进入阻塞状态
ii. 当线程等待另外一个线程通知调度器的一个条件是,它自己进入等待状态。(Object.wait(),Object.join()/java.util.concurrent中的Lock或者Condition)
-
Timed waitting(计时等待)
i. 有几个方法有一个超时参数。调用他们导致线程进入计时等待状态。(Thread.sleep,Object.wait,Thread.join,Lock.tryLock,Condition.await)
-
Terminated(被终止)
i. 因为run方法正常退出而自然死亡。
ii. 因为一个没有捕获的异常终止了run方法而意外死亡。
-
-
锁对象
-
ReentrantLock
myLock.lock(); try{ //代码块 } finally{ myLock.unlock(); }
把解锁操作括在finally子句之内是至关重要的,如果在临界区的代码抛出异常,锁必须被释放,否则其他线程将永远阻塞
锁是可重入的,因为线程可以重复地获得已经持有的锁。锁保持一个持有计数来跟踪对lock方法的嵌套调用。线程在每一次调用lock都要调用unlock来释放锁。 由于这一特性,被一个锁保护的代码可以调用另一个使用相同的锁的方法。
ReentrantLock(boolean fair) 设置锁的公平性,但是使用公平锁要慢很多。并且也无法确保线程调度器是绝对公平的。如果线程器选择忽略一个线程,而该线程为了这个锁已经等待了很长时间,那么就没有机会公平地处理这个问题了。
-
-
条件对象
条件对象/条件变量: 线程进入临界区,但是要满足某一个条件之后才能执行。这一条件被称之为条件对象/条件变量 。
注意如果用 if(condition code){}当前线程完全有可能在成功的完成测试,且在调用transfer方法之前被中断,当线程再次获得执行权后可能已经不满足条件了。但是之前通过了测试
//通常对await的调用应该在以下形式的循环体中 private Condition condition; ... while(!(ok to proceed)){ condition.await(); //线程被挂起 } condition。signalAll();//唤醒线程,被唤醒的线程并不会马上执行,仅仅是解除了等待线程的阻塞。
-
Synchronized 关键字
-
关于使用Lock和Condition对象还是使用同步方法来自《java核心》建议:
- 最好既不使用Lock/Condition也不使用Synchronized关键字,在许多情况下可以使用java.util.concurrent包中的一种机制,他会为你处理所有的加锁。
- 如果Synchronized关键字适合你的程序那么尽量使用它,这样可以减少编写代码的数量,减少处处哦的几率
- 如果特别需要Lock/Conditon结构提供的独有特性时,才使用Lock/Condition