什么是多线程
线程是操作系统能够进行运算调度的最小单位,而多线程就是指并发执行多个线程。多线程是为了同步完成多项任务,为了提高资源使用效率来提高系统的效率,也可以发挥多核处理器的优势。-
多线程的缺点
- 如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。
- 线程可能会给程序带来更多“bug”,需要注意线程安全的问题。
- 多线程情况下可能出现线程死锁情况。
- 线程的中止需要考虑其对程序运行的影响。
-
创建线程有哪些方式
常用的有两种- 1.继承Thread类覆写
run
方法 - 2.实现Runnable接口,并实现
run
方法。(启动线程需要以Runnable作为参数创建Thread对象调用start方法)
还可以通过Callable或者基于线程池(Executors)创建
- 1.继承Thread类覆写
-
多线程怎么同步
-
synchronized
通常使用synchronized
关键字来保证方法或代码块在多线程场景下的同步调用,但是使用synchronized
关键字的时候需要注意死锁问题。private synchronized void init() { //TODO }
-
ReentrantLock
使用ReentrantLock
时需要注意及时释放锁,否则可能会产生死锁的问题。通常在finally
关键字中释放,来保障即使出现异常情况,锁也可以正常释放。public class Task { private static final String TAG = "ThreadTask"; private ReentrantLock lock = new ReentrantLock(); /** * 消费 */ private void expense() { lock.lock();//获得锁 try { Log.d(TAG, "expense"); //TODO } finally { lock.unlock();//释放锁 } } }
-
-
4.什么是死锁?怎么解决?
-
死锁
死锁是至多个线程在运行时因争夺资源进入一种互相等待的状态。
如下代码,当有多个线程分别执行方法runA
和runB
时就容易产生死锁的情况,当A线程进入到runA
方法获取到object1
对象锁的时候,如果此时正好有线程B进入到runB
方法且获取到了object2
对象锁,此时线程A将等待线程B释放object2
对象锁,而线程B等待线程A释放object1
对象锁,导致谁都无法继续执行,进入死锁状态。private Object object1 = new Object(); private Object object2 = new Object(); private void runA() { synchronized (object1) { synchronized (object2) { Log.d(TAG, "runA"); } } } private void runB() { synchronized (object2) { synchronized (object1) { Log.d(TAG, "runB "); } } }
-
如何解决死锁问题
- 1.尽量不使用嵌套锁
- 2.不得已情况下使用嵌套锁时,使用顺序锁,不同的方法内即锁定的多个对象的顺序保持一致。
-
volatile
它的原理是每次要线程要访问volatile修饰 的变量时都是从内存中读取,而不是存缓存当中读取,因此每个线程访问到的变量值都是一样的。这样就保证了同步。-
终止线程
线程不能或者不建议直接终止,Thread
类的stop()
方法已废弃,在Android中调用会直接抛出异常。线程不应该粗暴的被停止,因为粗暴的停止那一刻,线程的可能还正在处理任务,持有的资源还未释放,会导致程序运行异常。@Deprecated public final void stop() { stop(new ThreadDeath()); } @Deprecated public final void stop(Throwable obj) { throw new UnsupportedOperationException(); }
正确的尝试终止或者中断线程的方法是调用
interrupt()
方法,或者直接调用Thread
类的静态方法Thread.interrupt()
,interrupt()
方法会给线程设置标记位,调用过该方法后,线程的interrupted()
方法将会返回true
。需要注意的是在调用interrupted()
方法后,线程的interrupted
标记将会被重置,即再次调用interrupt()
方法将会返回false
,安全的结束线程的方法可参考如下实现class Demo { public void run() { ThreadTask threadTask = new ThreadTask() threadTask.start() try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } threadTask.interrupt(); } class ThreadTask extends Thread { @Override public void run() { for (int i = 0; i < 1_000_000; i++) { if (interrupted()) { Log.d(TAG, "ThreadTask interrupted"); return; } //TODO } } } }
-
为什么
Thread.sleep()
方法需要添加try/catch
?try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
是因为在线程
sleep
的时候,该线程的interrupt
方法可能会被调用,当该线程的interrupt
方法被调用的时候就会抛出这个异常。抛出异常之后线程的interrupted
标记将会被重置,即再次调用interrupt()
方法将会返回false
。如果在调用sleep
之前线程已经被调用interrupt
方法,则执行到Thread.sleep()
时会直接抛出异常。
-
wait()
和notifyAll()
怎么用?wait()
方法可以让当前线程释放锁,进入等待队(等待被激活的线程队列), 当notifyAll()
被调用的时候,进入wait
的线程会被激活,然后继续执行wait()
之后的代码。
通常wait()
会和notifyAll()
配合使用,一般是涉及到多个线程使用共享资源的时候,notifyAll()
可以理解为通知其他线程共享资源已经准备好了,notify()
只会激活一个线程,notifyAll()
可以激活所有该对象被调用wait()
的线程。多个线程调用某个对象的wait()
方法后都会进入等待队列,等待notifyAll()
的调用。另外wait()
和notifyAll()
都是Object
的方法,因为这里涉及到的是线程锁,而这里锁住的是对象,不是线程,而wait
和notifyAll
的线程一般不是同一个线程,使用时wait()
和notifyAll()
方法被调用的对象一定要是同一个对象。以下代码为wait()
和notifyAll()
常规使用方法。public class Task { private String name; private static final String TAG = "ThreadTask"; private synchronized void init() { name = "AA"; notifyAll(); } private synchronized void logName() { //使用while,线程被激活后再次进行判断,因为线程被激活后name字段不一定是非空 while (name == null){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Log.d(TAG, "ThreadTask name is " + name); } public void start() { Thread thread1 = new Thread() { @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } logName(); } }; Thread thread2 = new Thread() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } init(); } }; thread1.start(); thread2.start(); } }
-
join
通常是在某个线程(A)内调用另一个线程(B)的join()
方法,相当于当前线程进入了一个不需要synchronized
的wait
状态,表示当前线程A执行到此接下来让B线程来执行,即把时间片交给了B线程,当B线程结束后,join()
方法之后的代码才会继续执行,如下代码public class Task { private String name; private static final String TAG = "ThreadTask"; private synchronized void init() { name = "AA"; } private synchronized void logName() { Log.d(TAG, "ThreadTask name is " + name); } public void start() { final Thread thread1 = new Thread() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } init(); } }; Thread thread2 = new Thread() { @Override public void run() { try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } logName(); } }; thread1.start(); thread2.start(); } }
-
yield
yield()
表示把当前线程的时间片交出去,给其他同优先级或者优先级较高的线程执行,保证其他线程的执行。代码如下:Thread thread = new Thread() { @Override public void run() { Thread.yield(); //TODO } };