目录
一、线程的概念
二、线程创建的方式及特点
三、线程创建方式
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只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。
创建方式 特点 继承Thread java中是单继承,具有局限性 实现Runnable接口 灵活,方便同一个对象被多个线程使用 实现Callable接口 Callable接口要重写的call()方法是可以有返回值的。
call()方法可以抛出异常,供外部捕获。
Callable接口支持泛型。
AsyncTask api 30(Android 11)中AsyncTask被正式废弃
1、继承Thread类
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyThread myThread=new MyThread(); myThread.start(); } class MyThread extends Thread{ @Override public void run() { Log.e("sjd====","开启子线程"); } }
当然,如果只是简单的开启子线程,也可直接使用匿名类开启
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(){ @Override public void run() { Log.e("sjd====","开启子线程"); } }.start(); }
2、实现Runnable接口
@Override protected 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{ @Override public void run() { Log.e("sjd====","开启子线程"); } }
当然,如果只是简单的开启子线程,也可直接使用匿名类开启
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Runnable runnable=new Runnable(){ @Override public void run() { Log.e("sjd====","开启子线程"); } }; Thread thread=new Thread(runnable); thread.start(); }
3、实现Callable接口(我觉得了解即可)
@Override protected 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
{ @Override public Integer call() throws Exception { Log.e("sjd====","开启子线程"); return null; } } 4、AsyncTask异步任务(被弃用)
onPreExecute 主线程中调用,任务开始前调用,可以做一些初始化操作,例如显示进度条 doInBackground 子线程中运行的方法,执行耗时任务,可以调用publishProgress方法来反馈执行进度 onProgressUpdate publishProgress调用之后,会调用onProgressUpdate onPostExecute 切换到主线程中执行的方法,更新UI 5、AsyncTask替代方案
- 使用 Executors 线程池替代,具体案例查看下面线程池的使用。
1、线程停止---true/false
- JDK推荐的stop()和destroy()已被弃用,我们可以自己设置标志位,通过true和false的方式让线程停止,这个比较简单,这里不再写示例。
2、线程休眠---sleep()
- 调用Thread.sleep(毫秒数),sleep时间到达时间之后,子线程又会进入到就绪状态
- 因为在java中,每个对象都有一个锁,sleep的时候不会释放锁
3、线程礼让---yield()
- 线程礼让,是指让当前正在执行的线程暂停,但是不阻塞,不会让出锁;
- 此时的线程从运行状态转变为就绪状态;
- 礼让能否成功,看当前CPU如何分配调度
- 示例:
@Override protected 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 { @Override public 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合并线程,等待当前插入的线程先执行,再执行其他线程,此时其他线程是阻塞状态;
- 示例:
@Override protected 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()
- 查看线程的活动状态
- 线程可以分为两种,一种是用户线程,一种是守护线程
- 守护线程必须要等所有的用户线程执行完,才会停止
- 虚拟机必须确保用户线程执行完,不用等守护线程执行完
- 典型的守护线程的用法就是垃圾回收线程
- 示例:
@Override protected 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 { @Override public void run() { while (true){ Log.e("sjd====","守护线程"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 运行结果:
在说线程同步之前,先举个非常典型的例子,卖火车票,当同时有多个人来买票的时候,假如不做任何限制,会有什么样的情况?
示例代码:
@Override protected 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;//判断是否还有余票 @Override public void run() { while (isGood) { //如果票数为0 if (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;//判断是否还有余票 @Override public void run() { while (isGood) { synchronized (this) { //如果票数为0 if (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对象
- 示例代码:
@Override protected 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(); @Override public void run() { while (isGood) { reentrantLock.lock(); try { //如果票数为0 if (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、线程通信的方法
2、java中的线程通信案例
wait() 线程挂起 notify() 唤醒线程 notifyall() 唤醒所有线程(优先级高的线程先被唤醒) 案例:
@Override protected 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 { @Override public 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(); } } }
结果分析:
- 首先是线程同步方法中执行;
- 此时只有线程a,当i=10的时候,没有阻塞可唤醒的线程;
- a线程循环输出1-50,i=50的时候,此时调用wait被阻塞,让出锁
- 线程b开始执行,i=10的时候,唤醒a线程;
- 虽然a线程被唤醒,但是b线程未执行完,所以b线程一直循环输出1-50
- b线程i=50的时候,b线程阻塞,a线程开始执行输出51-100;
- a线程结束,让出锁,b线程继续输出51-100
3、Android-handler通信
3.1、handler的通信机制
Message
消息 Hanlder
发送和处理消息,来处理main线程和其他线程之间的通信 Looper
负责循环从消息队列取消息 MessageQueue
消息队列,先进先出 3.2、handler常用的基础send方法
- sendEmptyMessage(int),发送空消息
- sendMessage(Message),发送message
- sendMessageAtTime(Message,long),在确定的时间发送消息
- sendMessageDelayed(Message,long),发送消息,延迟
- 示例:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { TextView tv_button; TextView tv_button1; @Override protected 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(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: String str= (String) msg.obj; tv_button1.setText(str); break; } } }; @Override public 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方法
- post(Runnable),把子线程的Runnable对象切换到主线程队列中,等待执行
- postAtTime(Runnable,long)
- postDelayed(Runnable,long)
- 示例:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { TextView tv_button; TextView tv_button1; @Override protected 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(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what){ case 1: String str= (String) msg.obj; tv_button1.setText(str); break; } } }; @Override public void onClick(View v) { switch (v.getId()) { case R.id.tv_button: new Thread(){ @Override public void run() { //子线程运行 handler.post(new Runnable() { @Override public void run() { tv_button1.setText("1231234123"); } }); } }.start(); break; } } }
点到源码进行查看
new 刚开始创建,初始化 runnable 准备状态
- ready:准备
- running:执行
blocked 阻塞状态 waiting 等待状态,调用wait方法 timed_waiting 超时等待,等固定时间就不等了 terminated 结束状态 1、验证new,runnable,terminated状态
@Override protected 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 { @Override public void run() { Log.e("sjd====","-3--"+Thread.currentThread().getState()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }
2、验证blocked状态
为什么下面加1秒的延时,是同步线程的时候,当线程2调用start方法之后,可能还没有调用,还没进入到阻塞状态,所以延迟1秒
@Override protected 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 { @Override public 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状态
@Override protected 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 { @Override public 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状态
@Override protected 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 { @Override public 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
- 可缓存线程池,如果线程池长度过处理需要,可灵活回收空闲线程,如果没有可回收的,则新建线程
- 示例代码:
@Override protected 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{ @Override public void run() { Log.e("sjd====",Thread.currentThread().getName()); } }
- 运行结果:(循环开启5个线程,但是线程1回收,又重新使用)
2.2、newFixedThreadPool
- 固定长度的线程池,可以控制线程最大的并发数,超出的线程在队列中等待
- 示例代码:
@Override protected 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{ @Override public void run() { Log.e("sjd====",Thread.currentThread().getName()); } }
- 运行结果:
2.3、newSingleThreadExecutor
- 创建一个单线程的线程池,只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行
2.3、newScheduledThreadPool
- 创建了一个定长线程池,支持定时以及周期性任务
- 造成死锁的原因,就是当多个线程各自占有一些资源,并且互相之间还需要其他线程占有的资源才能继续,而导致多个线程都在等待对方释放资源,这种都停止执行的情况,就可能出现死锁的问题。为了更好的理解,下面用图示进行演示:
- 上面的图示解释:多个线程互相抱着对方需要的资源,形成僵局
- 模拟死锁的场景:
- 假如两个人,一个人有面包,想喝牛奶,一个人有牛奶,想吃面包
@Override protected 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; } @Override public 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()+"先获得的面包的锁,等待牛奶的锁"); } } } } }
- 运行结果:
解决方案,就是不拿对方的锁
@Override protected 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; } @Override public 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()+"先获得的面包的锁,等待牛奶的锁"); } } } }
运行结果