一、线程和进程的区别
(进程是资源分配的最小单位,线程是CPU调度的最小单位)
在Java程序中,Java会对操作系统提供的功能进行封装,其中就有线程和进程,当我们运行一个程序的时候,就会产生一个进程,而一个进程至少包含一个线程。并且每个进程对应一个JVM实例,多个线程共享JVM的堆。Java中采用单线程编程模型,程序会自动创建主线程,并且主线程可以创建子线程,原则上要后于子线程完成执行。
二、Thread中Start()和Run()方法的区别
- Start()方法会调用JVM的StartThread方法创建一个新的子线程,并通过JVM的Thread_run来调用run方法
- run()方法只是Thread的一个普通方法,直接可以调用。
三、Thread和Runnable是什么关系
- Thread只是一个类,并且实现了Runnable的接口
四、如何实现处理线程的返回值
1、主线程等待法
public class CycleWait implements Runnable{
private String value;
public void run(){
try{
Thread.currentThread().sleep(5000);
}catch(Exception e){
e.printStackTrace();
}
value="we have data now";
}
public static void main(String[] args){
CycleWait c= new CycleWait();
Thread t1=new Thread(c);
t1.start();
while(c.value=null){//此处使用while循环来等待线程的结束
Thread.sleep(100);
}
System.out.println("value : "+value);
}
}优点:简单
缺点:需要自己书写等待循环的逻辑,并且循环多久也不确定
2、使用Thread类的join()阻塞当前线程以等待子线程处理完毕
public class CycleWait implements Runnable{
private String value;
public void run(){
try{
Thread.currentThread().sleep(5000);
}catch(Exception e){
e.printStackTrace();
}
value="we have data now";
}
public static void main(String[] args){
CycleWait c= new CycleWait();
Thread t1=new Thread(c);
t1.start();
t1.join();//使用join阻塞,来等待线程处理完毕
System.out.println("value : "+value);
}
}缺点:粒度不够细。如果join()之前有多个线程start(),则会统统阻塞。
补充:join的源码
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {//如果join()没有传值
while (isAlive()) {//isAlive()判断当前线程是否存活,如果存活就继续阻塞
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}join具体实现就是通过wait()来完成的。
3、通过Callable接口:通过FutureTask Or 线程池获取
public class MyCallable implements Callable<String> {//实现Callable接口
@Override
public String call() throws Exception {//该线程需要完成的任务
String value = "test";
System.out.println("Ready to work");
Thread.currentThread().sleep(5000);
System.out.println("Task done");
return value;
}
}public class FutureTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task=new FutureTask<String>(new MyCallable());
Thread thread = new Thread(task);
Thread.sleep(1000);
thread.start();
if(!task.isDone()){//判断任务是否完成
System.out.println("task has not finish, please wait!");
}
System.out.println("task reutrn : "+ task.get());//get会等待Callable有返回值之后才会返回
}
}五、线程的状态
1、新建(NEW),线程创建后尚未启动的状态,即还未调用start()方法
2、运行(Runnable):包括了Running和Ready。此状态的线程有可能正在执行,也又可能正在等待CPU为它分配执行时间。
3、无限等待(Waiting):不会被分配CPU执行时间,需要显式被唤醒。
未被设置Timeout参数的Object.wait()和Thread.join方法
4、限期等待(Timed Waiting):在一定时间后会由系统自动唤醒。
设置Timeout参数的Object.wait()和Thread.join方法
5、阻塞(Blocked):等待获取排它锁,例如:synchronized,当一方获取synchronized时,其他对象都要等待这个锁释放,等待的时候会进入阻塞状态。
6、结束:已终止线程的状态,线程已经结束执行
六、sleep和wait的区别
1、基本差别
- sleep()是Thread类的方法,wait()是Object类的方法
- sleep()方法可以在任何地方使用
- wait()方法只能在synchronized方法或synchronized块中使用
2、最主要的本质区别
- Thread.sleep()会让出CPU,不会导致锁行为的改变
- Object.wait()不会让出CPU,还会释放已经占有的同步资源锁
实例一:
public class WaitSleepDemo {
public static void main(String[] args) {
final Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread A is waiting to get lock.");
synchronized (lock) {
try {
System.out.println("Thread A get lock");
Thread.sleep(20);
System.out.println("Thread A do wait method");
lock.wait(1000);//让出锁10秒,这10秒中B线程获得锁来执行其代码逻辑
System.out.println("Thread A is done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread B is waiting to get lock.");
synchronized (lock) {
try {
System.out.println("Thread B get lock");
System.out.println("Thread B is sleeping 10 ms");
Thread.sleep(20);
System.out.println("Thread B is done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}实例二:
public class WaitSleepDemo {
public static void main(String[] args) {
final Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread A is waiting to get lock.");
synchronized (lock) {
try {
System.out.println("Thread A get lock");
Thread.sleep(20);
System.out.println("Thread A do wait method");
Thread.sleep(1000);
System.out.println("Thread A is done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread B is waiting to get lock.");
synchronized (lock) {
try {
System.out.println("Thread B get lock");
System.out.println("Thread B is sleeping 10 ms");
lock.wait(10);
System.out.println("Thread B is done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}此时A线程没有让出锁,则必须等待A执行完毕释放锁资源,B才会执行
七、notify和notifyall的区别
1、锁池EntryList
假如线程A已经拥有了某个对象的锁,线程B、C想要调用这个对象的synchronized方法,但是B、C线程在进入该方法之前,必须获得该对象的锁拥有权,而A又没有释放锁,此时B、C会进入阻塞状态,进入锁池来等待锁的释放。
2、等待池WaitSet
执行wait方法之后,该线程会进入等待池
notifyAll()会让等待池里所有线程进入锁池,来争夺锁资源
notify()则会随机选取一个出与等待池的线程进入锁池竞争获取锁的机会
八、yield
yield对锁行为没有影响,当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示。
九、interrup函数
调用interrup(),通知线程应该中断了。
1、如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个interrupException异常。
2、如果线程处于正常活动状态,那么会将该线程的中断标志设置为True,被设置中断标志的线程将继续正常运行,不受影响。
使用方法:
①在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
②如果线程处于正常活动状态,那么会将该线程的中断标志设置为true,被设置中断标志的线程将继续正常运行,不受影响。
十、FutureTask
1、FutureTask有两个构造器,一个接收Runnable,一个接收Callable
接收Runnable的构造器的目的只有一个,就是把入参都转化成Callable。因为Callable的功能比Runnable丰富。Callable的call方法有返回值,而Runnable的run方法没有。
public FutureTask(Runnable runnable, V result) {
// Executors.callable 方法把 runnable 适配成 RunnableAdapter,RunnableAdapter 实现了 callable,所以也就是把 runnable 直接适配成了 callable。
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
Executor.callable方法来把Runnable转化成Callable。
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}1、首先RunnableAdapter实现了Callable,所以RunnableAdapter就是Callable,,然后就会通过这种适配模式,new RunnableAdapter(Runnable r,T)返回RunnableAdapter对象,进而也就是Runnable变成Callable
2、get方法
无参get会一直等到任务执行完毕才返回。
有参get可以设定固定的时间,如果任务还没有执行成功,直接返回异常。
3、cancel方法主要是用来取消任务,如果任务还没有执行,是可以取消的,如果任务已经在执行过程中了,你可以选择不取消,或者直接打断执行中的任务。