(1)有三种方式可以用来创建线程:
1.继承Thread类
2.实现Runnable接口
3.应用程序可以使用Executor框架来创建线程池
三种方法相比较而言,实现Runnable接口更受欢迎,因为Java不支持多继承,所以如果类本身以及继承了别的类,只能选择实现Runnable接口来创建线程。
(2)线程的几种可用状态
新建(new):新创建一个线程对象;
可运行(runnable):线程对象创建后,其他线程来调用该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权。
运行(running):可运行状态状态的线程获取到CPU的使用权后,执行程序代码;
阻塞(block):线程由于某种原因放弃CPU的使用权,暂时停止运行,直到线程进入可运行状态,等待下一次获得CPU使用权以重新进入运行状态。
阻塞的三种情况:
1.等待阻塞:运行状态的线程执行.wait()方法,JVM会把该线程放入等待队列中。
2.同步阻塞:运行状态的线程在获取对象的同步锁时,如果该同步锁被别的线程占用,JVM就会把该线程放入锁池(lock pool)里;
3.其他阻塞:运行状态的线程执行Thread.sleep(long ms)或者t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I / O 处理完毕时,线程重新转入可运行( runnable )状态。
死亡(dead):线程run()、main()方法执行结束,则该线程结束生命周期。
(3)同步方法和同步代码块的区别
1.同步方法默认用this或者当前类对象作为锁;
2.同步代码块相比于同步方法更加细粒度,可以选择只在部分代码进行同步,同步方法会对整个方法进行同步。
(4)什么是死锁
两个线程或两个以上线程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是这些线程都陷入了无限的等待中。
(5)如何确保多个线程可以访问多个资源同时又不会死锁
多线程产生死锁的四个必要条件:
互斥条件:一个资源每次只能被一个进程使用。
保持和请求条件:一个进程因请求资源而阻塞时,对已获得资源保持不放。
不可剥夺性:进程已获得资源,在未使用完成前,不能被剥夺。
循环等待条件(闭环):若干进程之间形成一种头尾相接的循环等待资源关系。
只要破坏其中任意一个条件,就可以避免死锁,可以指定获取锁的顺序,并强制线程按照指定的顺序获取锁。