1.进程

进程与进程间独立,进程是运行中的程序,独立运行的程序单位。

2.线程

线程属于进程,线程是进程中的独立执行单元 ,多个线程共享同一块内存空间和一组系统资源。

3.线程的创建方式

1.继承 java.lang.Thread 类(线程子类)
2.实现 java.lang.Runnable 接口(线程执行类)
3.实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常
4.线程池创建 ExecutorService接口表示线程池

4.线程的状态

New:新建状态,新创建的线程,尚未调用start()方法,只有对象;
Runnable:运行状态,运行中的线程,已经调用start()方法,线程正在或即将执行run()方法(running 或 ready);运行状态和就绪状态 start方法后表示可运行,只有得到cpu才可运行。
Blocked:阻塞状态,运行中的线程,等待获取锁,而被挂起,暂不执行;
Waiting:等待状态,运行中的线程,因为sleep()join()等方法调用,进入等待;
Timed Waiting:计时等待状态,运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;sleep是不会释放锁的,wait会释放锁。
Terminated:终止状态,线程已终止,因为run( )方法执行完毕或者出现了异常。
多线程的相关学习 - 图1

5.线程实现通信

多个线程因为在同一个进程中,所以互相通信比较容易的。
核心方法:wait(),notify(),notifyAll();
注意:线程通信一定是多个线程在操作同一个资源才需要进行通信。
线程通信必须先保证线程安全,否则毫无意义,代码也会报错!

  1. // 三个线程存钱
  2. public synchronized void saveMoney(double money) {
  3. try{
  4. // 1.知道是谁来存钱
  5. String name = Thread.currentThread().getName();
  6. // 2.判断余额是否足够
  7. if(this.money > 0){
  8. // 5.等待自己,唤醒别人!
  9. this.notifyAll();
  10. this.wait();
  11. }else{
  12. // 3.钱没有,存钱
  13. this.money += money;
  14. System.out.println(name+"来存钱,存入了"+money+"剩余:"+this.money);
  15. // 4.等待自己,唤醒别人!
  16. this.notifyAll();
  17. this.wait();
  18. }
  19. }catch (Exception e){
  20. e.printStackTrace();
  21. }
  22. }
  23. // 两个线程取钱
  24. public synchronized void drawMoney(double money) {
  25. try{
  26. // 1.知道是谁来取钱
  27. String name = Thread.currentThread().getName();
  28. // 2.判断余额是否足够
  29. if(this.money > 0){
  30. // 3.账户有钱,有钱可以取
  31. this.money -= money;
  32. System.out.println(name+"来取钱"+money+"取钱后剩余:"+this.money);
  33. // 4.没钱,先唤醒别人,等待自己,。
  34. this.notifyAll();
  35. this.wait();
  36. }else{
  37. // 5.余额不足,没钱,先唤醒别人,等待自己,。
  38. this.notifyAll();
  39. this.wait();
  40. }
  41. }catch (Exception e){
  42. e.printStackTrace();
  43. }
  44. }

6.线程实现同步

作用:就是为了解决线程安全问题的方案。
线程同步解决线程安全问题的核心思想:让多个线程实现先后依次访问共享资源,这样就解决了安全问题。

6.1同步代码块

把出现线程安全问题的核心代码给上锁,每次只能一个线程进入。 执行完毕以后自动解锁,其他线程才可以进来执行。

  1. public void drawMoney(double moeny) {
  2. // 开始判断取钱逻辑
  3. // 1.先知道是谁来取钱
  4. // 同步方法,同步代码块 ,如果为静态方法,使用class类同步 例如Account.class
  5. String name = Thread.currentThread().getName();
  6. // 2.判断余额是否足够
  7. synchronized (this){
  8. if(this.moeny >= moeny){
  9. System.out.println(name+"来取钱,余额足够,吐出"+moeny);
  10. // 3.更新余额
  11. this.moeny -= moeny;
  12. System.out.println(name+"来取钱后,余额剩余"+ this.moeny);
  13. }else{
  14. System.out.println(name+"来取钱,余额不足!");
  15. }
  16. }
  17. }

6.2同步方法

把出现线程安全问题的核心方法给锁起来,每次只能一个线程进入访问,其他线程必须在方法外面等待。直接给方法加上一个修饰符 synchronized.

  1. public synchronized void drawMoney(double moeny) {
  2. // 开始判断取钱逻辑
  3. // 1.先知道是谁来取钱
  4. // 同步方法,同步代码块 ,如果为静态方法,使用class类同步 例如Account.class
  5. String name = Thread.currentThread().getName();
  6. // 2.判断余额是否足够
  7. if(this.moeny >= moeny){
  8. System.out.println(name+"来取钱,余额足够,吐出"+moeny);
  9. // 3.更新余额
  10. this.moeny -= moeny;
  11. System.out.println(name+"来取钱后,余额剩余"+ this.moeny);
  12. }else{
  13. System.out.println(name+"来取钱,余额不足!");
  14. }
  15. }

6.3Lock锁

java.util.concurrent.locks.Lock提供的锁。
加锁:lock() 释放锁:unlock()

  1. public void drawMoney(double moeny) {
  2. // 开始判断取钱逻辑
  3. // 1.先知道是谁来取钱
  4. // 使用Lock实现同步,注意lock锁住部分一定要用try catch finally,如果不使用如果锁住的部分出现异常,无法释放锁的资源
  5. String name = Thread.currentThread().getName();
  6. lock.lock(); //上锁
  7. try {
  8. // 2.判断余额是否足够
  9. if(this.moeny >= moeny){
  10. System.out.println(name+"来取钱,余额足够,吐出"+moeny);
  11. // 3.更新余额
  12. this.moeny -= moeny;
  13. System.out.println(name+"来取钱后,余额剩余"+ this.moeny);
  14. }else{
  15. System.out.println(name+"来取钱,余额不足!");
  16. }
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }finally {
  20. lock.unlock(); //解锁
  21. }
  22. }

7.线程死锁问题

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
死锁产生的四个必要条件:(四个条件都满足才会形成死锁)
1、资源被一个线程使用(占有)时,别的线程不能使用。
2、资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待情况,p1要p2的资源,p2要p1的资源。这样就形成了一个等待环路

  1. public class ThreadDead {
  2. public static Object resource01 =new Object();
  3. public static Object resource02=new Object();
  4. public static void main(String[] args) {
  5. new Thread(new Runnable(){
  6. @Override
  7. public void run() {
  8. synchronized (resource01){
  9. try {
  10. Thread.sleep(1000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. System.out.println("线程占用资源1");
  15. synchronized (resource02){
  16. System.out.println("线程占用资源2");
  17. }
  18. }
  19. }
  20. }).start();
  21. new Thread(new Runnable(){
  22. @Override
  23. public void run() {
  24. synchronized (resource02){
  25. try {
  26. Thread.sleep(1000);
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. }
  30. System.out.println("线程占用资源2");
  31. synchronized (resource01){
  32. System.out.println("线程占用资源1");
  33. }
  34. }
  35. }
  36. }).start();
  37. }
  38. }

8.volitale关键字

它是在别的线程进行改动后,他会通知别的线程有改动,然后更新之前的值。
所有的成员变量和静态变量都会存在主存中,主存中的变量可以被多个线程共享。每个线程都有专属于自己的空间,工作内存一开始存储的是成员变量的副本。所以很多时候线程访问的自己空间中的变量,其他线程改变值将不可见。
解决方案:
1.加锁:线程获取锁之后,会清空工作内存,从主内存中拷贝共享变量最新的值到自己的工作空间。
2.使用volatile关键字:使用关键字主要是因为修改值之后他会通知别的线程,然后进行替换,保证了修改后的可见性。
volatile 和 synchronized的区别:
1.volatile 只能修饰成员变量和静态变量,而synchronized可以修饰方法和代码块。
2.volatile 只保证数据的可见性,但不保证多线程的写操作,不保证线程安全。

  1. public class VolitaleDemo extends Thread{
  2. // private volatile boolean flag=false;
  3. private boolean flag=false;
  4. @Override
  5. public void run() {
  6. try {
  7. Thread.sleep(1000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. flag=true;
  12. System.out.println("flag="+flag);
  13. }
  14. public boolean isFlag() {
  15. return flag;
  16. }
  17. public void setFlag(boolean flag) {
  18. this.flag = flag;
  19. }
  20. }
  21. class MainDemo{
  22. public static void main(String[] args) {
  23. // VolitaleDemo volitaleDemo=new VolitaleDemo();
  24. // volitaleDemo.start();
  25. //
  26. // while(true){
  27. // if(volitaleDemo.isFlag()) {
  28. // System.out.println("主线程在执行中~~~~");
  29. // }
  30. // }
  31. VolitaleDemo volitaleDemo=new VolitaleDemo();
  32. volitaleDemo.start();
  33. while(true){
  34. synchronized (MainDemo.class){
  35. if(volitaleDemo.isFlag()) {
  36. System.out.println("主线程在执行中~~~~");
  37. }
  38. }
  39. }
  40. }
  41. }

9.线程安全问题

多个线程同时操作同一个共享资源的时候可能会出现线程安全问题。

  1. // 账户类
  2. public class Account {
  3. private String cardID;
  4. private double moeny;
  5. public void drawMoney(double moeny) {
  6. // 开始判断取钱逻辑
  7. // 1.先知道是谁来取钱
  8. String name = Thread.currentThread().getName();
  9. // 2.判断余额是否足够
  10. if(this.moeny >= moeny){
  11. System.out.println(name+"来取钱,余额足够,吐出"+moeny);
  12. // 3.更新余额
  13. this.moeny -= moeny;
  14. System.out.println(name+"来取钱后,余额剩余"+ this.moeny);
  15. }else{
  16. System.out.println(name+"来取钱,余额不足!");
  17. }
  18. }
  1. // 线程类:创建取钱线程对象的。
  2. public class DrawThread extends Thread{
  3. // 定义一个成员变量接收账户对象
  4. private Account acc ;
  5. public DrawThread(Account acc,String name){
  6. super(name);
  7. this.acc = acc;
  8. }
  9. @Override
  10. public void run() {
  11. // 小明 小红
  12. // 取钱100000
  13. acc.drawMoney(100000);
  14. }
  15. }
  1. public class ThreadSafe {
  2. public static void main(String[] args) {
  3. // a.创建一个共享资源账户对象!
  4. Account acc = new Account("ICBC-110" , 100000);
  5. // b.创建2个线程对象去账户对象中取钱
  6. Thread littleMing = new DrawThread(acc,"小明");
  7. littleMing.start();
  8. Thread litterRed = new DrawThread(acc,"小红");
  9. litterRed.start();
  10. }
  11. }