线程概念
1. 线程就是程序中单独顺序的流控制。线程本身不能运行,它只能用于程序中
2. 多线程: 单个程序中可以同时运行多个不同的线程执行不同的任务
3. 线程是程序内的顺序控制流,只能使用分配给程序的资源和环境
线程和进程
1. 一个进程可以有多个线程
2. 多个进程的内部数据和状态都是完全独立的,是不可以进行数据交换的;而多线程是共享一块内存空间和一组系统资源,有可能互相影响进程之间,可以进行数据交换,而且线程的切换比进程切换的负担要小
线程的生命周期
线程的创建
1. 继承java.lang.Thread类,并重写run()方法2. 实现java.lang.Runnable接口,并实现其run()方法
- 继承Thread类后,无法再继承其他类,但实现Runable()接口后还可以继承其他类
- 实现Runable接口,访问当前线程,需要使用Thread.currentThread()方法
- 继承Thread类,访问当前线程,使用this即可
线程的启动
//方式一 MyThread mt = new MyThread(); mt.start(); //方式二 MyThread2 mt2 = new MyThread2(); Thread thread = new Thread(mt2); thread.start();
线程进入阻塞
- 线程调用sleep()方法进入休眠状态
- 线程调用了一个阻塞时IO方法,在该方法返回之前,线程处于阻塞
- 线程试图获取一个同步锁(监视器),但是该锁正被其他线程所持有
- 线程在等待某个通知(notify)
线程离开阻塞
线程死亡
- 调用的sleep()方法时间到
- 调用的阻塞IO方法已经返回
- 成功获得到了同步锁
- 获得等待的通知
- run()方法执行完成,线程正常结束
- 线程抛出一个未捕获的Exception或Error
- 直接调用线程的stop方法(禁止使用)
- 可以使用isAlive()方法判断线程是否死亡,该方法在线程处于就绪、运行、阻塞三种状态时返回true,处于新建及死亡状态时返回false
- 线程一旦死亡,则不能再次调用start()方法让其重新执行。
join线程
Thread提供了让一个线程等待另一个线程完成的方法:join()。
当某个程序在执行过程中调用了其他线程的join()方法,则当前线程被阻塞,直到被join()方法加入的线程完成为止。
public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() +"\t" + i); if(i == 5) { TheThread t1 = new TheThread(); t1.start(); t1.join(); //开始执行t1线程 } } }
守护线程(Daemon Thread)
除了特别设置外,我们创建的线程都是非守护线程
前台线程死亡,后台线程会自动死亡
public class TheThread3 extends Thread{ @Override public void run() { while(true) { System.out.println("非守护线程"); try { this.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public static void main(String[] args) throws InterruptedException { TheThread3 t3 = new TheThread3(); t3.start(); for (int i = 0; i < 10; i++) { System.out.println(i); } }
//设置为守护线程: public static void main(String[] args) throws InterruptedException { TheThread3 t3 = new TheThread3(); t3.setDaemon(true); t3.start(); for (int i = 0; i < 10; i++) { System.out.println(i); } }
线程同步
当多个线程同时对同一个对象的实例变量进行操作时,会引起线程的同步问题!
模拟线程同步(这里用银行取款来模拟)
//User.java public class User extends Thread{ private Account account; private String name; private float money; public User(String name,Account account,float money) { this.name = name; this.account = account; this.money = money; } @Override public void run() { try { this.sleep(2000); //等待取款时间 account.getMoney(name,money); } catch (InterruptedException e) { e.printStackTrace(); } } }
线程同步的实现方式//模拟取款.java public static void main(String[] args) { Account account = new Account(); User u1 = new User("Tom",account,1500); User u2 = new User("Lucy",account,1500); u1.start(); u2.start(); }
//1.同步方法的方式 public synchronized void getMoney(String name,float money) { if(money > this.money) { System.out.println("余额不足,当前余额为:" + this.money); } else { this.money -= money; System.out.println(name + "取款成功,当前余额为" + this.money); } }
//2.同步代码块方式 public void getMoney(String name,float money) { synchronized (this) { if(money > this.money) { System.out.println("余额不足,当前余额为:" + this.money); } else { this.money -= money; System.out.println(name + "取款成功,当前余额为" + this.money); } } }
//3.同步锁(JDK>=1.5)方式 private final ReentrantLock lock = new ReentrantLock(); public void getMoney(String name,float money) { lock.lock(); //加锁 try{ if(money > this.money) { System.out.println("余额不足,当前余额为:" + this.money); } else { this.money -= money; System.out.println(name + "取款成功,当前余额为" + this.money); } } finally { lock.unlock(); //解锁 } }