Java多线程,Android多线程

article/2025/9/15 7:15:27

目录

一、线程的概念

二、线程创建的方式及特点

三、线程创建方式

1、继承Thread类

2、实现Runnable接口

3、实现Callable接口(我觉得了解即可)

4、AsyncTask异步任务(被弃用)

5、AsyncTask替代方案

四、线程的基础操作

1、线程停止---true/false

2、线程休眠---sleep()

3、线程礼让---yield()

4、线程插队---join()

5、 线程优先级---setPriority(int p)

6、 线程状态---isAlive()

五、守护线程

六、线程同步

1、线程同步的概念

2、synchronized

3、Lock

3.1、为什么使用lock会出现只有一个线程拿到锁?

 七、线程通信

1、线程通信的方法

3、Android-handler通信

3.1、handler的通信机制

3.2、handler常用的基础send方法

3.3、handler常用的高级post方法 

 八、线程的六个状态

1、验证new,runnable,terminated状态

2、验证blocked状态

3、验证waiting状态

4、验证timed_waiting状态

九、线程池

1、线程池的优点

2、线程池类型

2.1、newCachedThreadPool

2.2、newFixedThreadPool

2.3、newSingleThreadExecutor

十、死锁


一、线程的概念

  • 线程是操作系统能够进行运算调度的最小单位;
  • 一个进程中可以并发多个线程,每条线程并行执行不同的任务;
  • 很多多线程都是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器,如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。

二、线程创建的方式及特点

创建方式特点
继承Threadjava中是单继承,具有局限性
实现Runnable接口灵活,方便同一个对象被多个线程使用
实现Callable接口

Callable接口要重写的call()方法是可以有返回值的。

call()方法可以抛出异常,供外部捕获。

Callable接口支持泛型。

AsyncTaskapi 30(Android 11)中AsyncTask被正式废弃

三、线程创建方式

1、继承Thread类

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread myThread=new MyThread();myThread.start();}class MyThread extends Thread{@Overridepublic void run() {Log.e("sjd====","开启子线程");}}

  当然,如果只是简单的开启子线程,也可直接使用匿名类开启

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(){@Overridepublic void run() {Log.e("sjd====","开启子线程");}}.start();}

2、实现Runnable接口

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread myThread=new MyThread();Thread thread=new Thread(myThread);thread.start();}class MyThread implements Runnable{@Overridepublic void run() {Log.e("sjd====","开启子线程");}}

  当然,如果只是简单的开启子线程,也可直接使用匿名类开启 

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Runnable runnable=new Runnable(){@Overridepublic void run() {Log.e("sjd====","开启子线程");}};Thread thread=new Thread(runnable);thread.start();}

3、实现Callable接口(我觉得了解即可)

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread myThread = new MyThread();FutureTask futureTask = new FutureTask(myThread);new Thread(futureTask).start();}class MyThread implements Callable<Integer> {@Overridepublic Integer call() throws Exception {Log.e("sjd====","开启子线程");return null;}}

4、AsyncTask异步任务(被弃用

onPreExecute主线程中调用,任务开始前调用,可以做一些初始化操作,例如显示进度条
doInBackground子线程中运行的方法,执行耗时任务,可以调用publishProgress方法来反馈执行进度
onProgressUpdatepublishProgress调用之后,会调用onProgressUpdate
onPostExecute切换到主线程中执行的方法,更新UI

5、AsyncTask替代方案

  • 使用 Executors 线程池替代,具体案例查看下面线程池的使用。

四、线程的基础操作

1、线程停止---true/false

  • JDK推荐的stop()和destroy()已被弃用,我们可以自己设置标志位,通过true和false的方式让线程停止,这个比较简单,这里不再写示例。

2、线程休眠---sleep()

  • 调用Thread.sleep(毫秒数),sleep时间到达时间之后,子线程又会进入到就绪状态
  • 因为在java中,每个对象都有一个锁,sleep的时候不会释放锁

3、线程礼让---yield()

  • 线程礼让,是指让当前正在执行的线程暂停,但是不阻塞,不会让出锁
  • 此时的线程从运行状态转变为就绪状态;
  • 礼让能否成功,看当前CPU如何分配调度
  • 示例:
    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread myThread = new MyThread();Thread t1=new Thread(myThread,"子线程1");Thread t2=new Thread(myThread,"子线程2");t1.start();t2.start();}class MyThread implements Runnable {@Overridepublic void run() {try {Log.e("sjd====",Thread.currentThread().getName()+"开始");for (int i=0;i<100;i++){if (i==90){Thread.yield();}}Log.e("sjd====",Thread.currentThread().getName()+"结束");} catch (Exception e) {e.printStackTrace();}}}
  • 运行结果:(yiled()方法的作用是放弃当前CPU的资源,将资源让给其它线程,但放弃的时间不确定,有可能刚刚放弃,又马上获得了CPU时间片。所以下面的结果不是固定的,看CPU心情了~~~)

4、线程插队---join()

  • join合并线程,等待当前插入的线程先执行,再执行其他线程,此时其他线程是阻塞状态;
  • 示例:
    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread myThread = new MyThread();Thread t1=new Thread(myThread,"子线程---");t1.start();for (int i=0;i<5;i++){if (i==1){try {t1.join();Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}Log.e("sjd====",Thread.currentThread().getName()+"---"+i);}}
  • 运行结果:

5、 线程优先级---setPriority(int p)

  • 通过源码分析,最大为10,最小为1;
  • 按理说优先级越高的线程会优先执行,但是也不一定,看CPU调度

6、 线程状态---isAlive()

  • 查看线程的活动状态

五、守护线程

  •  线程可以分为两种,一种是用户线程,一种是守护线程
  • 守护线程必须要等所有的用户线程执行完,才会停止
  • 虚拟机必须确保用户线程执行完,不用等守护线程执行完
  • 典型的守护线程的用法就是垃圾回收线程
  • 示例:
    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread myThread = new MyThread();MyThread2 myThread2 = new MyThread2();Thread t1 = new Thread(myThread);Thread t2 = new Thread(myThread2);t1.setDaemon(true);t1.start();t2.start();}class MyThread implements Runnable {@Overridepublic void run() {while (true){Log.e("sjd====","守护线程");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}
  • 运行结果:

六、线程同步

在说线程同步之前,先举个非常典型的例子,卖火车票,当同时有多个人来买票的时候,假如不做任何限制,会有什么样的情况?

示例代码:

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread myThread = new MyThread();Thread t1 = new Thread(myThread, "用户一");Thread t2 = new Thread(myThread, "用户二");Thread t3 = new Thread(myThread, "用户三");t1.start();t2.start();t3.start();}class MyThread implements Runnable {int total = 10;//假设现在只剩下了10张火车票boolean isGood=true;//判断是否还有余票@Overridepublic  void run() {while (isGood) {//如果票数为0if (total <= 0) {isGood=false;Log.e("sjd====", Thread.currentThread().getName() + "没有买到票");return;}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Log.e("sjd====", Thread.currentThread().getName() + "买到了第" + total + "张火车票");total--;}}}

运行结果:(结果不唯一,多试几次)

很显然下面的运行结果是错误

这种情况下,就要用到线程同步

1、线程同步的概念

按照上面的例子进行理解,就是,当多个线程访问同一个对象,大家都在修改这个对象,造成了数据错误,这时候就需要用到线程同步,说白了,线程同步就是一种等待机制,等上一个线程操作完,再继续操作,形成一种队列的形式,避免数据错误。

2、synchronized

  • synchronized 是JVM层面的隐式锁,作用域外自动解锁,是Java关键字对象只有在同步块或同步方法中才能调用wait/notify方法
  • synchronized 不需要用户去手动释放锁,synchronized 代码执行完后系统会自动让线程释放对锁的占用
  • synchronized是不可中断类型的锁,除非加锁的代码中出现异常或正常执行完成
  • synchronzied锁的是对象,每个对象对应一把锁,每个synchronzied执行都要获取对象的锁,否则会线程阻塞,而且一旦执行,就自己独占这个对象的锁,直到执行结束,后面被阻塞的线程才能获取这个对象的锁,继续执行
  • 当两个并发线程访问同一个对象这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块
  • synchronzied的缺点就是如果修饰方法,会影响执行效率
  • synchronzie可修饰方法,也可以修饰代码块
  • sleep()需要放在同步块中,同步块中是为了多线程并发处理,按照顺序依次执行,sleep()方法就是想让当前线程进入阻塞状态,不释放锁,等到sleep()的时间到了,再进入就绪态,等待cpu调度。所以就不放在同步块中。
  • 示例代码:(把上面的代码稍作修改)
    class MyThread implements Runnable {int total = 10;//假设现在只剩下了10张火车票boolean isGood = true;//判断是否还有余票@Overridepublic void run() {while (isGood) {synchronized (this) {//如果票数为0if (total <= 0) {isGood = false;Log.e("sjd====", Thread.currentThread().getName() + "没有买到票");return;}Log.e("sjd====", Thread.currentThread().getName() + "买到了第" + total + "张火车票");total--;}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}
  • 运行结果:

3、Lock

  • lock是显示锁,显示的定义同步锁,只能修饰代码块,代码块锁
  • ReentrantLock是lock的实现类,和synchronzied有相同的作用,可以显示的进行加锁,释放锁
  • 每次只能有一个线程对lock对象加锁,线程开始访问共享资源之前应先获得lock对象
  • 示例代码:
        @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread myThread = new MyThread();Thread t1 = new Thread(myThread, "用户一");Thread t2 = new Thread(myThread, "用户二");Thread t3 = new Thread(myThread, "用户三");t1.start();t2.start();t3.start();}class MyThread implements Runnable {int total = 10;//假设现在只剩下了10张火车票boolean isGood = true;//判断是否还有余票ReentrantLock reentrantLock=new ReentrantLock();@Overridepublic void run() {while (isGood) {reentrantLock.lock();try {//如果票数为0if (total <= 0) {isGood = false;Log.e("sjd====", Thread.currentThread().getName() + "没有买到票");break;}Log.e("sjd====", Thread.currentThread().getName() + "买到了第" + total + "张火车票");total--;}finally {reentrantLock.unlock();}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}
  • 运行结果

3.1、为什么使用lock会出现只有一个线程拿到锁?

  • 锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁。

3.2、lock设置公平锁

  • ReentrantLock lock=new ReentrantLock(true);让线程排队,但是会比较消耗系统资源

3.3、lock和synchronized的比较

  • lock是显示锁,更灵活,默认是非公平锁,性能更好,扩展性更好
  • synchronized隐式锁,作用域外自动释放

 七、线程通信

1、线程通信的方法

wait()线程挂起
notify()唤醒线程
notifyall()唤醒所有线程(优先级高的线程先被唤醒)
2、java中的线程通信案例

案例:

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_button = (TextView) findViewById(R.id.tv_button);tv_button1 = (TextView) findViewById(R.id.tv_button1);tv_button.setOnClickListener(this);MyThread myThread = new MyThread();Thread t1 = new Thread(myThread, "a");Thread t2 = new Thread(myThread, "b");t1.start();t2.start();}class MyThread implements Runnable {@Overridepublic void run() {synchronized (this) {for (int i = 1; i <= 100; i++) {Log.e("sjd====", Thread.currentThread().getName() + "---" + i);if (i==10){notify();}if (i==50){try {wait();} catch (InterruptedException e) {e.printStackTrace();}}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}notify();}}}

结果分析:

  1. 首先是线程同步方法中执行;
  2. 此时只有线程a,当i=10的时候,没有阻塞可唤醒的线程;
  3. a线程循环输出1-50,i=50的时候,此时调用wait被阻塞,让出锁
  4. 线程b开始执行,i=10的时候,唤醒a线程;
  5. 虽然a线程被唤醒,但是b线程未执行完,所以b线程一直循环输出1-50
  6. b线程i=50的时候,b线程阻塞,a线程开始执行输出51-100;
  7. a线程结束,让出锁,b线程继续输出51-100

3、Android-handler通信

3.1、handler的通信机制

Message

消息

Hanlder

发送和处理消息,来处理main线程和其他线程之间的通信

Looper

负责循环从消息队列取消息

MessageQueue

消息队列,先进先出

3.2、handler常用的基础send方法

  1. sendEmptyMessage(int),发送空消息
  2. sendMessage(Message),发送message
  3. sendMessageAtTime(Message,long),在确定的时间发送消息
  4. sendMessageDelayed(Message,long),发送消息,延迟
  5. 示例:
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {TextView tv_button;TextView tv_button1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_button = (TextView) findViewById(R.id.tv_button);tv_button1 = (TextView) findViewById(R.id.tv_button1);tv_button.setOnClickListener(this);}Handler handler=new Handler(){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);switch (msg.what){case 1:String str= (String) msg.obj;tv_button1.setText(str);break;}}};@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.tv_button:Message message=new Message();message.what=1;message.obj="1231231";handler.sendMessageAtTime(message,2000);break;}}
    }

3.3、handler常用的高级post方法 

  1. post(Runnable),把子线程的Runnable对象切换到主线程队列中,等待执行
  2. postAtTime(Runnable,long)
  3. postDelayed(Runnable,long)
  4. 示例:
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {TextView tv_button;TextView tv_button1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_button = (TextView) findViewById(R.id.tv_button);tv_button1 = (TextView) findViewById(R.id.tv_button1);tv_button.setOnClickListener(this);}Handler handler=new Handler(){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);switch (msg.what){case 1:String str= (String) msg.obj;tv_button1.setText(str);break;}}};@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.tv_button:new Thread(){@Overridepublic void run() {//子线程运行handler.post(new Runnable() {@Overridepublic void run() {tv_button1.setText("1231234123");}});}}.start();break;}}
    }

 八、线程的六个状态

点到源码进行查看

new刚开始创建,初始化
runnable

准备状态

  • ready:准备
  • running:执行
blocked阻塞状态
waiting等待状态,调用wait方法
timed_waiting超时等待,等固定时间就不等了
terminated结束状态

1、验证new,runnable,terminated状态

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread myThread = new MyThread();Thread t1 = new Thread(myThread, "用户一");Thread t2 = new Thread(myThread, "用户二");Thread t3 = new Thread(myThread, "用户三");Log.e("sjd====","-1--"+t1.getState());t1.start();Log.e("sjd====","-2--"+t1.getState());
//        t2.start();
//        t3.start();try {Thread.sleep(2000);Log.e("sjd====","-4--"+t1.getState());} catch (InterruptedException e) {e.printStackTrace();}try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}Log.e("sjd====","-5--"+t1.getState());}class MyThread implements Runnable {@Overridepublic void run() {Log.e("sjd====","-3--"+Thread.currentThread().getState());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}}

2、验证blocked状态

为什么下面加1秒的延时,是同步线程的时候,当线程2调用start方法之后,可能还没有调用,还没进入到阻塞状态,所以延迟1秒

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_button = (TextView) findViewById(R.id.tv_button);tv_button1 = (TextView) findViewById(R.id.tv_button1);tv_button.setOnClickListener(this);MyThread myThread = new MyThread();Thread t1 = new Thread(myThread, "用户一");Thread t2 = new Thread(myThread, "用户二");t1.start();t2.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Log.e("sjd====1", "---" + t2.getName() + " " + t2.getState());}class MyThread implements Runnable {@Overridepublic void run() {synchronized (this) {for (int i = 0; i < 10; i++) {Log.e("sjd====", "---" + Thread.currentThread().getName() + " " + Thread.currentThread().getState() + " " + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}}

3、验证waiting状态

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_button = (TextView) findViewById(R.id.tv_button);tv_button1 = (TextView) findViewById(R.id.tv_button1);tv_button.setOnClickListener(this);MyThread myThread = new MyThread();Thread t1 = new Thread(myThread, "用户一");Thread t2 = new Thread(myThread, "用户二");t1.start();t2.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Log.e("sjd====1", "---" + t1.getName() + " " + t1.getState());Log.e("sjd====1", "---" + t2.getName() + " " + t2.getState());}class MyThread implements Runnable {@Overridepublic void run() {synchronized (this) {for (int i = 0; i < 10; i++) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}Log.e("sjd====", "---" + Thread.currentThread().getName() + " " + Thread.currentThread().getState() + " " + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}}

4、验证timed_waiting状态

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_button = (TextView) findViewById(R.id.tv_button);tv_button1 = (TextView) findViewById(R.id.tv_button1);tv_button.setOnClickListener(this);//        MyThread t1=new MyThread("男朋友",0);
//        MyThread t2=new MyThread("女朋友",1);
//
//        t1.start();
//        t2.start();MyThread myThread = new MyThread();Thread t1 = new Thread(myThread, "用户一");Thread t2 = new Thread(myThread, "用户二");t1.start();t2.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Log.e("sjd====1", "---" + t1.getName() + " " + t1.getState());Log.e("sjd====1", "---" + t2.getName() + " " + t2.getState());}class MyThread implements Runnable {@Overridepublic void run() {synchronized (this) {for (int i = 0; i < 10; i++) {try {wait(2000);} catch (InterruptedException e) {e.printStackTrace();}Log.e("sjd====", "---" + Thread.currentThread().getName() + " " + Thread.currentThread().getState() + " " + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}}

九、线程池

1、线程池的优点

  • 单独创建线程缺乏统一管理,线程过多,会造成资源占用过多,可能会导致死机或者oom;
  • 线程池可以使线程重用,控制线程的并发数,提高资源的使用率。

2、线程池类型

2.1、newCachedThreadPool

  • 可缓存线程池,如果线程池长度过处理需要,可灵活回收空闲线程,如果没有可回收的,则新建线程
  • 示例代码:
        @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ExecutorService pool= Executors.newCachedThreadPool();for (int i=0;i<5;i++){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}pool.execute(new Mythread());}pool.shutdown();}class Mythread implements Runnable{@Overridepublic void run() {Log.e("sjd====",Thread.currentThread().getName());}}
  • 运行结果:(循环开启5个线程,但是线程1回收,又重新使用)

2.2、newFixedThreadPool

  • 固定长度的线程池,可以控制线程最大的并发数,超出的线程在队列中等待
  • 示例代码:
        @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ExecutorService pool= Executors.newFixedThreadPool(2);for (int i=0;i<5;i++){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}pool.execute(new Mythread());}pool.shutdown();}class Mythread implements Runnable{@Overridepublic void run() {Log.e("sjd====",Thread.currentThread().getName());}}
  • 运行结果:

2.3、newSingleThreadExecutor

  • 创建一个单线程的线程池,只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行

2.3、newScheduledThreadPool

  • 创建了一个定长线程池,支持定时以及周期性任务

十、死锁

  • 造成死锁的原因,就是当多个线程各自占有一些资源,并且互相之间还需要其他线程占有的资源才能继续,而导致多个线程都在等待对方释放资源,这种都停止执行的情况,就可能出现死锁的问题。为了更好的理解,下面用图示进行演示:
  • 上面的图示解释:多个线程互相抱着对方需要的资源,形成僵局
  • 模拟死锁的场景:
  • 假如两个人,一个人有面包,想喝牛奶,一个人有牛奶,想吃面包
    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyThread t1=new MyThread("男朋友",0);MyThread t2=new MyThread("女朋友",1);t1.start();t2.start();}//面包static class Bread{}//牛奶static class Milk{}static class MyThread extends Thread{static Bread bread=new Bread();static Milk milk=new Milk();String who;//角色int choice;//选择哪个MyThread(String who,int choice){this.who=who;this.choice=choice;}@Overridepublic void run() {if (choice==0){synchronized (bread){Log.e("sjd====",Thread.currentThread().getName()+"先获得的面包的锁,等待牛奶的锁");try {Thread.sleep(1000);//一秒之后想喝牛奶} catch (InterruptedException e) {e.printStackTrace();}synchronized (milk){Log.e("sjd====",Thread.currentThread().getName()+"先获得的牛奶的锁,等待面包的锁");}}}else {synchronized (milk){Log.e("sjd====",Thread.currentThread().getName()+"先获得的牛奶的锁,等待面包的锁");try {Thread.sleep(1000);//一秒之后想吃面包} catch (InterruptedException e) {e.printStackTrace();}synchronized (bread){Log.e("sjd====",Thread.currentThread().getName()+"先获得的面包的锁,等待牛奶的锁");}}}}}
  • 运行结果:

 解决方案,就是不拿对方的锁

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_button = (TextView) findViewById(R.id.tv_button);tv_button1 = (TextView) findViewById(R.id.tv_button1);tv_button.setOnClickListener(this);MyThread t1=new MyThread("男朋友",0);MyThread t2=new MyThread("女朋友",1);t1.start();t2.start();}//面包static class Bread{}//牛奶static class Milk{}static class MyThread extends Thread{static Bread bread=new Bread();static Milk milk=new Milk();String who;//角色int choice;//选择哪个MyThread(String who,int choice){this.who=who;this.choice=choice;}@Overridepublic void run() {if (choice==0){synchronized (bread){Log.e("sjd====",Thread.currentThread().getName()+"先获得的面包的锁,等待牛奶的锁");try {Thread.sleep(1000);//一秒之后想喝牛奶} catch (InterruptedException e) {e.printStackTrace();}}synchronized (milk){Log.e("sjd====",Thread.currentThread().getName()+"先获得的牛奶的锁,等待面包的锁");}}else {synchronized (milk){Log.e("sjd====",Thread.currentThread().getName()+"先获得的牛奶的锁,等待面包的锁");try {Thread.sleep(1000);//一秒之后想吃面包} catch (InterruptedException e) {e.printStackTrace();}}synchronized (bread){Log.e("sjd====",Thread.currentThread().getName()+"先获得的面包的锁,等待牛奶的锁");}}}}

运行结果


http://chatgpt.dhexx.cn/article/AihjCkDM.shtml

相关文章

Android多线程开发详解

一、基本概念 1、时间片轮转机制 如果在时间片结束时进程还在运行&#xff0c;则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结来,则CPU当即进行切换。调度程序所要做的就是维护一张就绪进程列表,当进程用完它的时间片后,它被移到队列的末尾。 每个进程被分…

Android开发中四种常用的多线程实现方式

前言 一般来说&#xff0c;一个应用至少有一个进程&#xff0c;一个进程至少有一个线程。线程是CPU调度的基本单位&#xff0c;进程是系统资源分配的基本单位。 进程拥有独占的内存资源&#xff0c;一个进程可以看作一个JVM一个进程崩溃后&#xff0c;一般不会影响保护模式下…

Android 中的多线程简介

一、概念讲解 进程&#xff1a;是程序运行过程中系统进行资源分配和调度的一个独立单位&#xff0c;使多个程序可 并发执行&#xff0c;以提高系统的资源利用率和吞吐量。 线程&#xff1a;一个基本的CPU执行单元 & 程序执行流的最小单元。 线程自己不拥有系统资源&#…

anchor free和anchor based的区别

链接&#xff1a;https://www.zhihu.com/question/356551927/answer/926659692 1.目标检测算法一般可分为anchor-based、anchor-free、两者融合类&#xff0c;区别就在于有没有利用anchor提取候选目标框。A. anchor-based类算法代表是fasterRCNN、SSD、YoloV2/V3等fasterRCNN-…

Anchor based and Anchor free(无锚VS有锚)【总结】

anchor-free 和 anchor-based 区别 anchor-free和anchor-based是两种不同的目标检测方法&#xff0c;区别在于是否使用预定义的anchor框来匹配真实的目标框。 anchor-based方法使用不同大小和形状的anchor框来回归和分类目标&#xff0c;例如faster rcnn、retinanet和yolo等。a…

2 anchor-base和anchor_free两者的优缺点

anchor-base和anchor_free两者的优缺点 anchor-base和anchor_free两者的优缺点 一、什么是anchor二、anchor-base和anchor-free的区别三、anchor-free和single anchor三、anchor-base和anchor-free的优缺点 参考 一、什么是anchor 从字面的意思解释&#xff0c;anchor就是船锚…

Anchor-Free总结

目录 Anchor-Free综述 一. CornerNet 1.1 概述1.2 模块介绍 1.2.1 Heatmap1.2.2 Offset1.2.3 Grouping Corners1.2.4 Corner Pooling1.3 总结二. CenterNet 2.1 概述2.2 Center-Regression三. FCOS 3.1. 概述3.2. 模块介绍 3.2.1 论文思路简介3.3.2 回归形式3.3 参考文献四 ATS…

【AI面试】Anchor based 、 Anchor free 和 no anchor 的辨析

深度学习的目标检测算法,通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整(回归)区域边界,从而更准确地预测目标的真实边界框(ground-truth bounding box)。 目标检测算法会需要做两个事情: 推荐区域框是否有目标(positive or …

一文读懂anchor-base和anchor-free

1. 从Faster-RCNN看Anchor Faster-RCNN相对于Fast-RCNN的一个改进是引入了RPN网络&#xff0c;RPN用于区域推荐&#xff0c;替换了此前的SS算法使得网络在整体上更加的CNN化。那么RPN是怎么进行区域推荐的&#xff1f; 简单来说RPN先列举出数万个矩形框&#xff0c;然后用卷积…

目标检测3--AnchorFree的FCOS

文章目录 1.介绍2.FCOS中使用的方法2.1 网络结构2.2FCOS中使用FPN的多级预测2.3FCOS中的中心度 3.mmdetection中FCOS源码参考资料 欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 1.介绍 论文:《FCOS: Fully Convolutional One-Stage Obj…

浅谈Anchor-Free发展历程

1.早期探索&#xff1a; DenseBox: https://arxiv.org/abs/1509.04874 YOLO: https://arxiv.org/abs/1506.02640 2.基于关键点&#xff1a; CornerNet: https://arxiv.org/abs/1808.01244 ExtremeNet: https://arxiv.org/abs/1901.08043 3.密集预测: FSAF: https://arxiv.org/a…

Anchor-Free系列之FCOS:A Simple and Strong Anchor-free Object Detector

Anchor-Free系列之CornerNet: Detecting Objects as Paired Keypoints_程大海的博客-CSDN博客 Anchor-Free系列之CenterNet&#xff1a;Objects as Points_程大海的博客-CSDN博客 Anchor-Free系列之FCOS&#xff1a;A Simple and Strong Anchor-free Object Detector_程大海的…

Anchor Based和Anchor Free

Anchor Based和Anchor Free之间区别主要有以下两点&#xff1a;1.分类差异&#xff08;关键正负样本定义&#xff09;2.回归差异 1.分类差异&#xff1a; 现阶段的算法多尺度预测&#xff0c;即GT是由哪一个特征层和位置Anchor预测。 Anchor Based是由IoU来确定哪层和哪个位置…

解读《Bridging the Gap Between Anchor-based and Anchor-free Detection》

张士峰大佬近期发了一篇论文解读Anchor-base和Anchor-free方法间的差别&#xff0c;其本质在于正负样本的选取方式不同。 论文&#xff1a;《Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection》 链接&#xff1a;ht…

anchor-free方法总结

cornernet&#xff0c;centernet&#xff0c;onenet&#xff0c;fcos 这几篇论文的引用关系&#xff08;提出先后顺序&#xff09;&#xff1a; 将按照上面的顺序&#xff0c;从背景、标签分配等方面说明区别于联系。 一、背景&#xff1a; Cornernet&#xff1a;认为使用a…

anchor free和anchor base

仅供个人学习使用 1、anchor base anchor base的方法需要先在图片上生成候选框&#xff0c;无论是RPN生成还是通过k-means生成的先验框&#xff0c;都需要在分类回归之前有存在的框可使用。在框的基础上进行之后的操作。 超参数较为难调&#xff0c;正负样本不平衡&#xff…

Anchor free的心得

问题&#xff1a; 没有了Anchor框的监督信息&#xff0c;我们怎么针对检测任务做到正确回归&#xff1f; 本质&#xff1a;样本与ground truth的对应&#xff0c;如何选择合适样本与真实场景对应 Anchor&#xff1a; 其加入降低了回归问题难度&#xff0c;为分类问题提供选择…

Anchor-based 与 Anchor-free

参考 Anchor-based 与 Anchor-free - 云社区 - 腾讯云 1. Feature Selective Anchor-Free Module for Single-Shot Object Detection 参考&#xff1a;CVPR2019 | CMU提出Single-Shot目标检测最强算法&#xff1a;FSAF 2. FCOS: Fully Convolutional One-Stage Object Det…

Anchor-free

找到了一个说在工业领域很好的 目标检测 下面几篇paper有异曲同工之妙&#xff0c;开启了anchor-based和anchor-free的轮回。 1. Feature Selective Anchor-Free Module for Single-Shot Object Detection 2. FCOS: Fully Convolutional One-Stage Object Detection 3. Fo…

AnchorFree系列算法详解

目录 前言一、Anchor-Based方法回顾二、Anchor Free系列方法简介1. Anchor Free系列算法历史2. Anchor free经典算法详解2.1. 基于关键点的Anchor Free检测算法1. CornerNet 2. 2 基于中心的Anchor Free检测算法1. FCOS2. CenterNet3. TTFNet -- CenterNet的改进版 3. AnchorFr…