1、进程和线程之间的区别
线程是资源分配的最小单位,线程是CPU调度的最小单位。进程是抢占处理机的调度单位,拥有自己的虚拟内存空间,线程只属于某个进程,并与进程内的其他线程共享进程的资源;线程只由堆栈寄存器、程序计数器和线程控制表(TCB)组成。总结:
- 线程不能看作独立的应用,而进程可以看作独立的应用
- 进程有独立的地址空间,互不影响,线程只是进程的不同执行路径
- 线程没有单独的地址空间,多进程的程序比多线程程序健壮
- 进程的切换比线程的切换开销大
2、Java进程和线程的关系
- Java对操作系统的提供的功能进行封装,包括进程和线程
- 运行一个程序会产生一个进程,进程至少包含一个线程
- 每个进程对应一个JVM实例,多个线程共享JVM里面的堆
- Java采用单线程编程模型,程序会自动创建主线程
- 主线程可以创建子线程,原则上要后于子线程执行完成
3、Thread中的start和run方法的区别
调用start()方法会创建一个新的子线程并启动,而run()方法只是Thread的一个调用当前线程执行的方法。
public class ThreadTest {
private static void attack(){
System.out.println("fight");
System.out.println("current thread is:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t = new Thread(){
public void run(){
attack();
}
};
System.out.println("current main thread is :" + Thread.currentThread().getName());
t.start();/*结果为current main thread is :main;fight;current thread is:Thread-0*/
t.run();/*结果为current main thread is :main;fight;current thread is:main*/
}
}
4、Thread和Runnable是什么关系
- Thread是实现了Runnable接口的类,使得run方法能够有多线程的特性
- 因为类的单一继承原则,推荐多使用Runnable接口实现多线程
/*使用继承Thead类实现多线程*/
package com.javabasic.bytecode.thread;
public class MyThread extends Thread {
private String name;
public MyThread(String name){
this.name = name;
}
@Override
public void run(){
for (int i = 0; i <= 10; i++){
System.out.println("this is " + name + ",i=" + i);
}
}
public static void main(String[] args) {
MyThread myThread1 = new MyThread("thread1");
MyThread myThread2 = new MyThread("thread2");
MyThread myThread3 = new MyThread("thread3");
myThread1.start();
myThread2.start();
myThread3.start();
}
}
/*使用实现Runnable接口实现多线程*/
package com.javabasic.bytecode.thread;
public class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
for (int i = 0; i <= 10; i++){
System.out.println("this is " + name + ",i=" + i);
}
}
public static void main(String[] args) {
MyRunnable myRunnable1 = new MyRunnable("runnable1");
MyRunnable myRunnable2 = new MyRunnable("runnable2");
MyRunnable myRunnable3 = new MyRunnable("runnable3");
Thread thread1 = new Thread(myRunnable1);
Thread thread2 = new Thread(myRunnable2);
Thread thread3 = new Thread(myRunnable3);
thread1.start();
thread2.start();
thread3.start();
}
}
5、如何处理线程的返回值
三种方式:
- 主线程等待法,即可让主线程循环等待,知道目标子线程有返回值为止;
public class CycleWait implements Runnable {
private String value;
@Override
public void run() {
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "we have data now";
}
public static void main(String[] args) throws InterruptedException {
CycleWait cycleWait = new CycleWait();
Thread thread = new Thread(cycleWait);
thread.start();
while (cycleWait.value == null){
Thread.currentThread().sleep(100);
}//主线程循环等待
System.out.println("value:" + cycleWait.value);
}
}
- 使用Thread类中的join()阻塞当前线程以等待子线程处理完毕,和第一个方法思想一样;
public class CycleWait implements Runnable {
private String value;
@Override
public void run() {
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "we have data now";
}
public static void main(String[] args) throws InterruptedException {
CycleWait cycleWait = new CycleWait();
Thread thread = new Thread(cycleWait);
thread.start();
thread.join();//调用join方法,使主线程等待
System.out.println("value:" + cycleWait.value);
}
}
- 通过Callable接口实现类,然后通过FutureTask或者是线程池获取:
//通过FutureTask类的get()方法来获取返回值
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
String value = "test";
System.out.println("Ready to work");
Thread.currentThread().sleep(5000);
System.out.println("test is done");
return value;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<String>(new MyCallable());
new Thread(futureTask).start();
if (!futureTask.isDone()){
System.out.println("plase wait");
}
System.out.println("task is return" + futureTask.get());
}
}
通过线程池来处理返回值
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
String value = "test";
System.out.println("Ready to work");
Thread.currentThread().sleep(5000);
System.out.println("test is done");
return value;
}
public static void main(String[] args) {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
Future<String> future = newCachedThreadPool.submit(new MyCallable());
if (!future.isDone()){
System.out.println("task is not finished, please wait");
}
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally {
newCachedThreadPool.shutdown();//关闭线程池
}
}
}
6、线程的6个状态
- 新建(New):创建后仍未启动的线程状态
- 运行(Runnable):包含Running和Ready
- 无限期等待(Waiting):不会被分配CPU执行时间,需要显示被唤醒;实现方式:调用没有设置参数的Thread.join()和Object.wait()方法,或者调用LockSupport.park()方法
- 有限期等待(Timed Waiting):在一定时间后被系统自动唤醒;实现方式:Thread.sleep()方法;设置了TimeOut参数的Thread.join()和Object.wait()方法;LockSupport.parkNanos()和LockSupport.parkUntil()方法
- 阻塞(Blocked):等待获取排他锁
- 结束(Terminated):已终止线程的状态,线程已经执行结束
7、sleep()和wait()的区别
- sleep()是Thread类的方法,wait()是Object类的方法
- sleep()方法是在任意地方可以使用
- wait()方法只能在synchronized方法或者synchronized块中使用
- 最主要的本质区别:Thread.sleep()只会让出CPU,不会让线程释放锁,不会导致锁行为的改变;Object.wait()不仅会让当前线程让出CPU,还会释放同步资源锁
注:wait()方法可以通过notify和notifyAll方法来唤醒
package com.javabasic.bytecode.thread;
public class WaitSleepDemo {
public static void main(String[] args) {
final Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
try {
System.out.println(" ThreadA01");
Thread.sleep(20);
System.out.println("ThreadA02");
lock.wait(1000);
System.out.println("ThreadA03");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}).start();
try{
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
try{
System.out.println("ThreadB01");
Thread.sleep(10);
System.out.println("ThreadB02");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}).start();
}
}
8、notify和notifyAll的区别
- notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
- notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会
9、yieId
当调用Thread.yieId()函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器会忽略这个暗示。
10、如何中断线程
通过调用interrupt(),通知线程应该中断了。如果线程处于被阻塞状态,那么线程立刻退出被阻塞状态,并抛出一个InterruptedException异常;如果线程处于正常活动状态,那么会将该线程的中断标志设置为true,被设置中断标志位的线程将继续照常运行,不受影响。所以interrupt()方法需要被调用的线程配合中断:在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程;如果线程处于正常活动状态,那么会将该线程的中断标志设置为true,被设置中断标志位的线程将继续照常运行,不受影响。
package com.javabasic.bytecode.thread;
public class InterruptDemo {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
int i = 0;
try{
//在正常运行任务时,经常检查本线程的中断标志位,如果被设置为了中断标志位就自行停止线程
while (!Thread.currentThread().isInterrupted()){
Thread.sleep(100);
i++;
System.out.println(Thread.currentThread().getName() + "(" + Thread.currentThread().getState() + ") loop" + i );
}
}catch (InterruptedException e){
//在调用阻塞方法时正确处理InterruptedException异常。
System.out.println(Thread.currentThread().getName() + "(" + Thread.currentThread().getState() + ") catch InterruptedException");
}
}
};
Thread thread1 = new Thread(runnable);
System.out.println(thread1.getName() + "(" + thread1.getState() + ") is new");
thread1.start();//启动线程
System.out.println(thread1.getName() + "(" + thread1.getState() + ") is started");
Thread.sleep(300);
thread1.interrupt();//发出中断指令
System.out.println(thread1.getName() + "(" + thread1.getState() + ") is Interrupted");
Thread.sleep(300);
System.out.println(thread1.getName() + "(" + thread1.getState() + ") is Interrupted now");
}
}