参考资料:https://redspider.gitbook.io/concurrent/
进程和线程的区别
进程是一个独立的运行环境,而线程是在进程中执行的一个任务。他们两个本质的区别是是否单独占有内存地址空间及其它系统资源(比如I/O):
-
进程单独占有一定的内存地址空间,所以进程间存在内存隔离,数据是分开的,数据共享复杂但是同步简单,各个进程之间互不干扰;而线程共享所属进程占有的内存地址空间和资源,数据共享简单,但是同步复杂。
-
进程单独占有一定的内存地址空间,一个进程出现问题不会影响其他进程,不影响主程序的稳定性,可靠性高;一个线程崩溃可能影响整个程序的稳定性,可靠性较低。
-
进程单独占有一定的内存地址空间,进程的创建和销毁不仅需要保存寄存器和栈信息,还需要资源的分配回收以及页调度,开销较大;线程只需要保存寄存器和栈信息,开销较小。
线程组(ThreadGroup)
每个Thread必然存在于一个ThreadGroup中,Thread不能独立于ThreadGroup存在。执行main()方法线程的名字是main,如果在new Thread时没有显式指定,那么默认将父线程(当前执行new Thread的线程)线程组设置为自己的线程组。
Start和run的区别
Start()会创建一个新的子线程并启动
Run()只是Thread的一个普通方法的调用
Thread和Runnable关系
Thread是实现了Runnable接口的类,使得run支持多线程
因类的单一继承原则,推荐多使用Runnable接口
1 public static void main(String[] args) { 2 3 new Thread(new Runnable() { 4 public void run() { 5 System.out.println("Runnable running.."); 6 } 7 }) { 8 public void run() { 9 System.out.println("Thread running.."); 10 }; 11 }.start(); 12 }
输出结果为
Thread running..
继承Thread类,那么在调用start的方法时会去调用Thread的子类的方法
如何给run()方法传参
构造函数传参
成员变量传参
回调函数传参
如何实现处理线程的返回值
主线程等待法(新建一个属性来存返回值,当这个属性还没值的时候,就等待,直到它有值)
使用Thread类的join()阻塞当前线程以等待子线程处理完毕
通过Callable接口实现,通过FutureTask 或线程池获取(推荐)
Sleep和wait区别
Sleep是Thread类的方法,wait是Object类的方法
Sleep方法可以在任何地方使用
Wait方法只能在synchronized方法或synchronized块中使用
Thread.sleep只会让出CPU,不会导致锁行为的改变
Object.wait不仅让出CPU,还会释放已经占有的同步资源锁
1 public class ThreadTest { 2 public static void main(String[] args) { 3 final Object lock = new Object(); 4 new Thread(new Runnable() { 5 @Override 6 public void run() { 7 System.out.println("A is waiting to get lock"); 8 synchronized (lock) { 9 try { 10 System.out.println("A get lock"); 11 Thread.sleep(20); 12 System.out.println("A get do wait method"); 13 Thread.sleep(1000);//只会让出CPU,不会导致锁行为的改变 14 System.out.println("A is done"); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } 19 } 20 }).start();; 21 try { 22 Thread.sleep(10);// 让A先执行 23 } catch (InterruptedException e1) { 24 e1.printStackTrace(); 25 } 26 new Thread(new Runnable() { 27 @Override 28 public void run() { 29 System.out.println("B is waiting to get lock"); 30 synchronized (lock) { 31 try { 32 System.out.println("B get lock"); 33 System.out.println("B is sleeping 10 ms"); 34 lock.wait(10);//不仅让出CPU,还会释放已经占有的同步资源锁 35 System.out.println("B is done"); 36 } catch (InterruptedException e) { 37 e.printStackTrace(); 38 } 39 } 40 } 41 }).start();; 42 } 43 }
等待池
假设线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁
Notify和notifyAll区别
notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会
yield
当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示
Interrupt
如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
如果线程处于正常活动状态,那么会将该线程的中断标志设为true,被设置中断标志的线程将继续正常运行,不收影响。