Java多线程学习总结
Java多线程学习总结
- 线程的概念
- 线程的创建
- 线程的操作
- 多线程(对象锁、信号、控制)
1.线程的概念
讨论线程我们需要明白四个概念:
线程:CPU进行资源调度的最小单位,与进程中其他线程共享资源;
多线程:指的是这个程序(一个进程)运行时产生了不止一个线程;
并行:同一时刻同时执行;
并发:通过cpu调度算法,表现为一段时间上的一起执行;
1.1.线程的状态
新建状态(new)
就绪状态(Runnable)
运行状态(Running)
阻塞状态(Blocked)
死亡状态(Dead)
2.线程的创建
Java中线程的创建主要分为三种方式:常见的两种为继承Thread类、实现Runnable接口,除此之外,使用ExecutorService、Callable、Future实现有返回结果的多线程(线程池相关知识);
2.1.继承Thread类
通过继承Thread类,重新实现run函数,完成线程的创建
public class TestThread extends Thread{
@Override
public void run(){
while(true){
TestUnit.print();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
2.2.实现Runnable接口
任何一个类实现了Runnable接口都可以看成线程的执行单元,可以通过Thread对象或线程池对象来完成对其的控制;
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
TestUnit.print();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
2.3.使用ExecutorService、Callable、Future实现
实现Callable接口的call方法后,可作为线程池(ExecutorService)的执行单元,注册执行;Future用于获取call方法执行结果;
//TestCallable.java
package com.aaron.concurrent.test;
import java.util.concurrent.Callable;
public class TestCallable implements Callable<Object> {
private int taskNum = -1;
public TestCallable(int i) {
this.taskNum = i;
}
public TestCallable() {
// TODO Auto-generated constructor stub
}
@Override
public Object call() throws Exception {
if(this.taskNum != -1){
return "My number is : "+ this.taskNum;
}
else{
return " I have no number!";
}
}
}
//MainCallable.java
package com.aaron.concurrent.test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class MainCallable {
public static final int TASK_NUM = 5;
public static final int MAX_TIME_OUT = 2;
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService pool = Executors.newFixedThreadPool(TASK_NUM);
List<Future> returnList = new ArrayList<Future>();
for (int i = 0; i < TASK_NUM; i++) {
Callable c = new TestCallable(i);
Future f = pool.submit(c);
returnList.add(f);
}
// 关闭线程池 ,等待线程执行完毕
pool.awaitTermination(MAX_TIME_OUT, TimeUnit.SECONDS);
pool.shutdown();
for (Future f : returnList) {
// 从Future对象上获取任务的返回值,并输出到控制台
System.out.println(f.get().toString());
}
}
}
3.线程的操作
3.1.启动线程:start()
start() 方法来启动线程,线程进入就绪状态,并不是马上执行run函数中的代码;
3.2.等待线程执行结束:join()
在代码中调用ThreadA.join(),会等待ThreadA执行完毕后才会执行join之后的代码
3.2.等待线程执行结束:join()
在代码中调用ThreadA.join(),会等待ThreadA执行完毕后才会执行join之后的代码
3.3.暂停:休眠(sleep)和等待(wait)
sleep和wait方法都能是线程进入阻塞状态,不过还是有不同之处,sleep方法不会交出线程的同步锁(monitor),依然参加时间片的轮转,只是不执行sleep以后的代码,同时sleep属于静态方法,只对调用它的线程有效,如Thread.sleep(100);而wait方法会释放同步锁,线程进入等待池中,不占用cpu,只用等到执行notify操作,才会唤醒该线程进入就绪状态;
3.3.让步:yield
yield函数使得线程放弃cpu使用权,从运行状态进入就绪状态,由系统进行重新调度,在实际使用中可能会没有效果,如只有一个线程;
3.4.唤醒:nitify和notifyAll
nitify函数可以唤醒正在共享对象等待池中处于阻塞状态的线程,重新进入到就绪队列,nitifyAll则是唤醒所有的阻塞线程;如果等待池中线程为空或者依旧无法获取到同步锁,则没有效果;
3.5.伪中断:interrupt
interrupt()函数只会修改线程运行的状态,并不会直接关闭线程;并通过异常的方式,提供程序员处理线程关闭的后续工作;如果线程本身处于阻塞状态(sleep/join/wait、io)会直接抛出InterruptedException 异常;否则,除非主动检查线程运行状态,interrupt()将不会有效果,也不会抛出异常;
4. 多线程(对象锁、信号、控制)
4.1.关键字(synchronized)
synchronized关键字有三个关键的问题:
1.哪里能使用
2.锁住的内容
3.锁住的对象
4.2.锁机制(Lock接口)
synchronized关键字锁的获取和释放由硬件底层来完成,简单却不灵活,而Java提供的一些锁机制由程序员来来控制,比synchronized要灵活很多;
可以说锁是各种读写控制策略的抽象,程序员可以了解并使用Java提供的读写锁,但是也能根据具体的场景扩展读写规则;
常用的Java读写锁:重入锁(ReentrantLock)、ReentrantReadWriteLock(读写锁)
//--Lock接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
//--ReadWriteLock接口
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing
*/
Lock writeLock();
}
锁相关的概念(和锁机制有关):可重入锁、可中断锁、公平锁、读写锁
4.3.多线程相关概念
4.3.1.死锁
锁没有释放或者同时请求同一个资源,解决一般的思路是锁的资源尽可能少,对资源的访问尽量是有序的;
4.3.1.线程安全和线程安全的集合类
当多个线程访问一个对象,如果不考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象是线程安全的;
Java提供一些常用的线程安全集合类(java.util.concurrent包):ConcurrentHashMap、ConcurrentLinkedDeque、CopyOnWriteArrayList、StringBuffer;
上一篇: 设计模式 之 组合模式
下一篇: 范文程是忠臣还是叛徒?揭秘其生平事迹