Handler退出,线程退出

Handler

looper退出

当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。

当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。

无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了。

handler内存泄漏 
在finish()的时候,该Message还没有被处理,Message持有Handler,Handler持有Activity,这样会导致该Activity不会被回收,就发生了内存泄露.
解决方法 
通过程序逻辑来进行保护。
    如果Handler中执行的是耗时的操作,在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。 
    如果Handler是被delay的Message持有了引用,那么在Activity的onDestroy()方法要调用Handler的remove*方法,把消息对象从消息队列移除就行了。 
            - 关于Handler.remove*方法
                        - `removeCallbacks(Runnable r)` ——清除r匹配上的Message。    
                        - `removeC4allbacks(Runnable r, Object token)` ——清除r匹配且匹配token(Message.obj)的Message,token为空时,只匹配r。
                        - `removeCallbacksAndMessages(Object token)` ——清除token匹配上的Message。
                        - `removeMessages(int what)` ——按what来匹配     
                        - `removeMessages(int what, Object object)` ——按what来匹配      
                        我们更多需要的是清除以该Handler为target的所有Message(Callback)就调用如下方法即可handler.removeCallbacksAndMessages(null);
线程

线程对象属于一次性消耗品,一般线程执行完run方法之后,线程就正常结束了,线程结束之后就报废了,不能再次start,只能新建一个线程对象。但有时run方法是永远不会结束的。例如在程序中使用线程进行Socket监听请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。当需要结束线程时,如何退出线程呢?

有三种方法可以结束线程:

1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止

2. 使用interrupt()方法中断线程

3. 使用stop方法强行终止线程(不推荐使用,可能发生不可预料的结果)

前两种方法都可以实现线程的正常退出,也就是要谈的优雅结束线程;第3种方法相当于电脑断电关机一样,是不安全的方法。


1使用退出标志终止线程

使用一个变量来控制循环,例如最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。代码如下:

public class ThreadSafe extends Thread {
    public volatile boolean exit = false; 
        public void run() { 
        while (!exit){
            //do something
        }
    } 
} 

定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值,

2使用interrupt()方法终止线程

使用interrupt()方法来终端线程可分为两种情况:

线程处于阻塞状态,如使用了sleep,同步锁的wait,socket的receiver,accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,系统会抛出一个InterruptedException异常,代码中通过捕获异常,然后break跳出循环状态,使线程正常结束。通常很多人认为只要调用interrupt方法线程就会结束,实际上是错的,一定要先捕获InterruptedException异常之后通过break来跳出循环,才能正常结束run方法。

public class ThreadSafe extends Thread {
    public void run() { 
        while (true){
            try{
                    Thread.sleep(5*1000);阻塞5妙
                }catch(InterruptedException e){
                    e.printStackTrace();
                    break;//捕获到异常之后,执行break跳出循环。
                }
        }
    } 
} 

线程未进入阻塞状态,使用isInterrupted()判断线程的中断标志来退出循环,当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。

public class ThreadSafe extends Thread {
    public void run() { 
        while (!isInterrupted()){
            //do something, but no tthrow InterruptedException
        }
    } 
}

为什么要区分进入阻塞状态和和非阻塞状态两种情况了,是因为当阻塞状态时,如果有interrupt()发生,系统除了会抛出InterruptedException异常外,还会调用interrupted()函数,调用时能获取到中断状态是true的状态,调用完之后会复位中断状态为false,所以异常抛出之后通过isInterrupted()是获取不到中断状态是true的状态,从而不能退出循环,因此在线程未进入阻塞的代码段时是可以通过isInterrupted()来判断中断是否发生来控制循环,在进入阻塞状态后要通过捕获异常来退出循环。因此使用interrupt()来退出线程的最好的方式应该是两种情况都要考虑:

public class ThreadSafe extends Thread {
    public void run() { 
        while (!isInterrupted()){ //非阻塞过程中通过判断中断标志来退出
            try{
                Thread.sleep(5*1000);//阻塞过程捕获中断异常来退出
            }catch(InterruptedException e){
                e.printStackTrace();
                break;//捕获到异常之后,执行break跳出循环。
            }
        }
    } 
} 

3使用stop方法终止线程

程序中可以直接使用thread.stop()来强行终止线程,但是stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,不安全主要是:thread.stop()调用之后,创建子线程的线程就会抛出ThreadDeatherror的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。因此,并不推荐使用stop方法来终止线程。


  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用多线程Handler可以实现输入数字并列举其质数的功能。首先,我们可以创建一个输入线程,用于接收用户输入的数字。然后,我们创建一个计算线程,用于计算输入数字的质数。 在输入线程中,我们可以使用一个循环来不断接收用户输入的数字,直到用户输入"exit"为止。每次接收到用户输入的数字,我们使用Handler将数字发送给计算线程进行质数判断。 在计算线程中,我们可以使用一个方法来判断一个数字是否为质数。在这个方法中,我们可以使用一个循环,从2开始迭代到该数字的平方根。如果该数字能够被任何一个迭代数整除,那么该数字就不是质数。如果迭代到最后都没有能整除的数,那么该数字就是质数。如果是质数,我们使用Handler将其发送回主线程进行显示。 通过使用多线程Handler的方式,我们可以实现输入数字,并在计算线程中实时判断并列举质数。这样可以提高程序的响应速度和用户体验。具体实现的代码如下所示: ```java public class PrimeNumberActivity extends AppCompatActivity { private EditText inputText; private Button startButton; private TextView resultText; private Handler handler; private Thread inputThread; private Thread calculateThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_prime_number); inputText = findViewById(R.id.input_text); startButton = findViewById(R.id.start_button); resultText = findViewById(R.id.result_text); handler = new Handler(); startButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startCalculation(); } }); } private void startCalculation() { calculateThread = new Thread(new Runnable() { @Override public void run() { int number = Integer.parseInt(inputText.getText().toString()); for (int i = 2; i <= number; i++) { if (isPrime(i)) { final int primeNumber = i; handler.post(new Runnable() { @Override public void run() { resultText.append(primeNumber + " "); } }); } } } }); calculateThread.start(); } private boolean isPrime(int number) { for (int i = 2; i <= Math.sqrt(number); i++) { if (number % i == 0) { return false; } } return true; } } ``` 这样,当用户点击开始按钮后,程序将启动计算线程,并对输入的数字进行质数判断,并使用Handler将质数发送回主线程进行显示。同时,用户可以通过输入线程输入新的数字或输入"exit"来退出程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值