一
1.进程和线程的区别
1.一个程序由一个或多个进程组成,一个进程由一个或多个线程组成。
2.不同进程之间是独立的地址空间、独立的资源占用如内存、I/O、cpu等。但同一进程的不同线程共享本进程的地址空间和资源。
3.两者均可并发执行。但进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,或者要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程。
2.java进程和线程的关系
图:java进程和线程的关系.png
3.并发和并行的区别
1.并发(Concurrent):指一个处理器交替处理多个任务。
2.并行(Parallel):指多个处理器同时处理多个任务,不交替执行。
二.
Thread的start和run方法的区别
图:Thread种start和run方法的区别.png
1.start()是启动一个新线程,run()是这个新线程的执行内容,一但这个新线程获得了CPU的使用权,将会自动执行run方法里面的内容。
2.如果不调用start()而直接调用run,run()方法的内容将会被当前线程执行。
三.
1.Thread和Runnable的区别
图:Thread和Runnable的区别.png
1.Thread是一个类,Runnable是一个接口,Thread实现了Runnable接口,并重写了run方法。
2.使用继承Runnable接口的方式创建线程需要依靠Thread的实例启动线程(Thread的构造方法允许传入Runnable实现类的实例),因为只有Thread中有start方法。
3.继承Thread后不能继承别的类,实现Runnable后还能实现别的接口。
四.
1.如何实现处理线程的返回值
图:如何实现处理线程的返回值.png
1.主线程等待发:启动子线程后,编写一个循环,循环中每次暂停n毫秒,一直等到子线程的值返回后,在接着执行主线程的逻辑。
while(null==值){
Thread.currentThread().sleep(n);
}
2.使用Thread的join()方法阻塞当前线程,等待子线程的执行完。
-------子线程.join();
3.通过实现Callable接口达成,需要搭配FutureTask或者 线程池
FutureTask
-------1.重写call()方法,相当于run方法,线程执行的内容。
-------2.需要依靠FutureTask类,FutureTask类实现了Runnable接口。将实现了Callable接口的类的实现类传入FutureTask的构造器中
-------3.将FutureTask类的实例传入Thread的构造器。
-------4.futureTask.isDone():判断执行call()的线程是否执行完成
-------5.futureTask.get():等待执行call()的线程执行完成在执行futureTask.get()方法,获取call()的返回值。
blic static void main(String[] args) throws ClassNotFoundException, InterruptedException, ExecutionException {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<String>(myCallable);
Thread thread = new Thread(futureTask);
thread.start();
if(!futureTask.isDone()){
System.out.println("还没执行完成");
}
System.out.println(futureTask.get());
线程池,方法功能同上
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
Future<String> future = threadPool.submit(new MyCallable());
if(!future.isDone()){
System.out.println("还没执行完成");
}
System.out.println(future.get());
}
五.
1.线程的状态
图:线程的状态.png
1.新建(New):创建后尚未启动的线程。
2.运行(Runnable):包含就绪(Ready)和运行(Running).
1.就绪(Runnable):调用了线程的start方法后,该线程位于线程池中,等待线程调度分配cpu的使用。
进入就绪状态的情况:
-------0.Thread.start()的线程
-------1.Thread.sleep(时间)时间结束的线程
-------2.Object.wait(时间)时间结束的线程
-------3.被分配到cpu正在执行的线程调用yield()方法并被线程调度器执行后
-------4.调用其他线程join(时间)的线程等待其他线程join(时间)结束的线程
-------5.Object.notify(),Object.notifyAll(),LockSupport.unPark(Thread)方法被唤醒的线程
-------6.锁池里拿到对象锁后的线程。
2.运行(Running):处于就绪状态的线程被分配到了cpu。
3.阻塞(Blocked):等待获取排他锁的线程会进入阻塞状态(存放在对象锁的锁池中)。
4.等待(Waitting):处于此状态的线程如果没有被其他线程唤醒,会一直处于等待状态
1.进入等待状态的情况:
-------1.执行Object.wait(),LockSupport.park()方法的线程
-------2.调用其他线程Object.join()方法的线程(在其他线程任务完成后此线程会进入就绪状态)
5.超时等待(Timed_Waiting):处于超时等待的线程在给定时间到达后自动回到就绪状态。
1.进入超时等待状态的情况:
-------1.执行了Thread.sleep(long),Object.wait(long),LockSupport.parkNanos(),LockSupport.parkUntil()方法的线程
-------2.调用其他线程的Thread.join(long)的线程。
6.终止(Terminated):执行完毕的线程进入此状态。
七
每个对象都包含一个锁池和等待池。
1.锁池(Entry Set)
图:锁池.png
多个线程同时申请某个对象的内部锁(Monitor)时,只有一个线程能申请到,未申请到的线程会进入阻塞状态(blocked),会被存入此对象的锁池中。当持有锁的线程释放锁时,锁池中任意一个线程会被唤醒进入运行状态(Runnable),此线程会与其他运行状态的线程(非锁池的线程)继续争取这个对象的内部锁,如果此线程成功争取到锁,会被移出锁池,失败的线程继续存入锁池。
2.等待池(Wait Set)
图:等待池.png
当某个线程获取了某个对象的内部锁,此线程执行此对象的wait()方法后,此线程会释放获得的对象的内部锁,然后进入此对象的等待池中。直到执行此对象的notify()/notifyAll()时,那么此对象的等待池的任意一个线程/所有线程将会进入运行状态(Runnable),继续竞争对象锁,如果等待池中被唤醒的某个线程成功争取到了锁,将会移出等待池。此存疑问:从等待池进入运行状态的线程竞争失败后,会继续回到等待池。
注:如果某个对象并未被某个线程获取内部锁而调用wait方法,会抛出java.lang.IllegalMonitorStateException异常。
八
1.sleep和wait的区别
图:sleep和wait的区别.png
1.sleep是Thread类的静态方法,wait是Object类的实例方法。
2.sleep和wait执行后,都会进入等待状态(等待或超时等待),但是sleep不会释放对象锁。而wait会释放对象锁。
3.wait不会发生死锁,sleep会 当获取了一个对象的内部锁锁的线程进入sleep,其他线程必须等待此线程sleep过后释放锁才能继续获取锁。
2.notify和notifyAll的区别
图:notify和notifyAll的区别.png
都是Object的实例方法。
八.
1.Thread的yield方法
图:Thread的yield方法.png
九.
1.中断线程方法
1.都属于Thread的实例方法。
图:被抛弃的方法.png
stop,suspend,resume.
1.suspend():暂停任务,不会释放锁,会发生死锁。
2.resume():恢复任务,恢复suspend()的线程。
3.stop():停止任务,该方法在终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序在可能在工作状态不确定的情况下工作。
图:使用的方法.png
1.interrupt:在一个线程中调用另一个线程的interrupt()方法,即会向那个线程发出信号——线程中断状态已被设置。但不会立即终端此线程的执行。对synchronized无效
2.用来判断当前线程的中断状态是否被设置(true or false)。
3.是Thread的static方法,用来恢复中断状态
2.LockSupport
1.LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
2.LockSupport操作的线程都会与一个许可(permit)关联。
3.调用一次unpark,permit就加1,调用一次park,permit会减1.当permit为0时,线程会进入阻塞状态,这时调用unpark会把permit置为1,同时park立即返回。无论调用多少次unpark,permit的值最高为1.
4.park()和unpark()不会有 Thread.suspend 和 Thread.resume 所可能引发的死锁问题,由于许可的存在,调用 park 的线程和另一个试图将其 unpark 的线程之间的竞争将保持活性。5.其他带时间的方法
-------1.parkNanos(long nanos) :park上增加超时返回。
-------2.parkUntil(long deadline): 阻塞当前线程,知道 deadline的时间。
-------3.unpark(Thread thread): 唤醒处于阻塞状态的线程。