【多线程】

多线程

        • 多线程
          • 1.守护线程
          • 2.线程的生命周期
          • 3.线程同步机制
          • 4.互斥锁
          • 5.线程死锁
          • 6.释放锁

多线程

1.守护线程

用户线程:也称为工作线程

守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程也自动结束

常见的守护线程:垃圾回收机制

public class ThreadMethod03 {
    public static void main(String[] args) throws InterruptedException {
        MyDaemonThread myDaemonThread = new MyDaemonThread();
        // 如果我们希望main线程结束后,子线程自动结束
        // TODO:需要将子线程设为守护线程即可
        myDaemonThread.setDaemon(true);
        myDaemonThread.start();

        for (int i = 1; i <= 10; i++) {
            System.out.println("宝强在辛苦的工作~~~" + i);
            Thread.sleep(1000);
        }
        System.out.println("主线程结束!!!");
    }
}

class MyDaemonThread extends Thread {
    @Override
    public void run() { // 无限循环
        for (int i = 1; ; i++) {
            try {
                Thread.sleep(1000); // 休眠50毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("马蓉和宋哲快乐聊天,哈哈哈~~~~" + i);
        }
    }
}

2.线程的生命周期
  • new

  • runnable 可运行的由内核进行调度

    • ready 就绪状态
    • running 运行状态
  • blocked

  • terminated 终止状态

  • waiting

  • timed_waiting 超时等待

    1.先进入new状态 2.再进入runnable状态 3.Thread.sleep()可能导致进入time_waiting状态 4.结束进程则进入terminated状态

    public class ThreadState {
        public static void main(String[] args) throws InterruptedException {
            T t = new T();
            System.out.println(t.getName() + " 的状态:" + t.getState());
            t.start();
            while (Thread.State.TERMINATED != t.getState()) {
                System.out.println(t.getName() + " 的状态:" + t.getState());
                Thread.sleep(1000);
            }
            System.out.println(t.getName() + " 的状态:" + t.getState());
        }
    }
    
    class T extends Thread {
        @Override
        public void run() {
            while (true) {
                for (int i = 0; i < 10; i++) {
                    System.out.println("hi " + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                break;
            }
        }
    }
    
    
    3.线程同步机制
    1. 在多线程中 保证数据在任何一个时刻 最多由一个线程访问

    2. 当有一个线程对内存进行操作时,其他线程都不能对这个内存地址进行操作

    实现线程同步的具体方法: Synchronized

    1. 同步代码块
    synchronized (对象){  // 对象锁
        // 需要被同步的代码
    }
    
    1. 放在方法声明中 方法成为同步方法
    pulbic synchronized void m(){
           // 需要被同步的代码
    }
    

    未使用同步机制售票导致 超卖

    public class SellTicket {
        public static void main(String[] args) {
            SellTicketThread sellTicketThread01 = new SellTicketThread();
            SellTicketThread sellTicketThread02 = new SellTicketThread();
            SellTicketThread sellTicketThread03 = new SellTicketThread();
            sellTicketThread01.start();
            sellTicketThread02.start();
            sellTicketThread03.start();
        }
    }
    
    class SellTicketThread extends Thread {
        private static int ticketNum = 100;
    
        @Override
        public void run() {
            while (true) {
                if (ticketNum <= 0) {
                    System.out.println("窗口: " + Thread.currentThread().getName() + "售票结束!!!");
                    break;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("窗口" + Thread.currentThread().getName() + ":售出一张票" + ",剩余" + (--ticketNum) + "张");
            }
        }
    }
    
    

    上述方法存在超卖问题:

    【多线程】_第1张图片

    使用synchronzied 解决超卖问题

    public class SellTicket {
        public static void main(String[] args) {
            SellTicketThread02 sellTicketThread02 = new SellTicketThread02();
            new Thread(sellTicketThread02).start();
            new Thread(sellTicketThread02).start();
            new Thread(sellTicketThread02).start();
        }
    }
    
    
    class SellTicketThread02 implements Runnable {
        private int ticketNum = 100;
        private boolean loop = true; // 控制run() 的执行
    
        public synchronized void sell() { // 同步方法,在同一时刻,只能有一个线程来执行sell方法
            if (ticketNum <= 0) {
                System.out.println("窗口: " + Thread.currentThread().getName() + "售票结束!!!");
                loop = false;
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口" + Thread.currentThread().getName() + ":售出一张票" + ",剩余票数=" + (--ticketNum) + "张");
        }
    
        @Override
        public void run() {
            while (loop) {
                sell();  // sell方法是一个同步方法
            }
        }
    }
    
    

    效果如下
    【多线程】_第2张图片

4.互斥锁
  1. java中引进了对象互斥锁来共享数据操作的完整性
  2. 对象使用互斥锁标记 保证任意时刻只能有一个线程访问该对象
  3. synchronized关键字与互斥锁联系
  4. 同步的局限性:导致程序执行效率降低
  5. 同步方法(静态):当前类.class
  6. 同步方法(非静态):可以是this,也可以是其他对象(要求是同一个对象)

synchronized 同步代码块

public void sell() { 
synchronized  (this)  {
        if (ticketNum <= 0) {
            System.out.println("窗口: " + Thread.currentThread().getName() + "售票结束!!!");
            loop = false;
            return;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("窗口" + Thread.currentThread().getName() + ":售出一张票" + ",剩余票数=" + (--ticketNum) + "张");
    }
}
public synchronized static void m1() {
} //锁加在当前类

public static void m2() {
synchronized (SellTicketThread02.class) {
  System.out.println("m2");
}
}

静态同步方法,锁在当前类

5.线程死锁

多个线程都占用了对方的锁资源,但不肯相让,导致死锁

如:妈妈让小明完成作业后,再玩手机

​ 小明要先玩手机,再写作业

/**
 * 模拟线程死锁
 */
public class DeadLock {
    public static void main(String[] args) {
        // 模拟死锁现象
        DeadLockDemo A = new DeadLockDemo(true);
        DeadLockDemo B = new DeadLockDemo(false);
        A.setName("线程A");
        B.setName("线程B");
        A.start();
        B.start();
    }
}

class DeadLockDemo extends Thread {
    static Object o1 = new Object(); // static 保证多线程共享一个对象
    static Object o2 = new Object();
    boolean flag;

    public DeadLockDemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        // 业务逻辑分析
        // 1. flag 为 true , 线程A先得到o1对象锁,然后尝试去获取o2对象锁
        // 2. 线程A 得不到o2 对象锁,就会blocked
        // 3. flag 为 false , 线程B先得到o2对象锁,然后尝试去获取o1对象锁
        // 2. 线程B 得不到o1 对象锁,就会blocked


        if (flag) {
            synchronized (o1) {  // 对象互斥锁 下面是同步代码
                System.out.println(Thread.currentThread().getName() + " 进入1");
                synchronized (o2) {
                    System.out.println(Thread.currentThread().getName() + " 进入2");
                }
            }
        } else {
            synchronized (o2) {
                System.out.println(Thread.currentThread().getName() + " 进入3");
                synchronized (o1) {
                    System.out.println(Thread.currentThread().getName() + " 进入4");
                }
            }
        }
    }
}

【多线程】_第3张图片

6.释放锁

释放锁的时机:

  1. 当前线程的同步方法,同步代码块执行结束
  2. 当前线程在同步代码块,同步方法中遇到break,return
  3. 当前线程在同步代码块,同步方法中出现了未处理的Error或Exception,导致异常结束
  4. 当前线程在同步代码块,同步方法中执行了线程对象的wait()方法,当前线程暂停并释放锁

不会释放锁的情况:

  1. 程序调用Thread.sleep(),Thread.yield(),暂停当前线程的执行,不会释放锁
  2. 其他线程调用该线程的suspend()将线程挂起,并不释放锁suspend()和resume()过时了

思考题
【多线程】_第4张图片

import java.util.Scanner;

/**
 *  作业01
 */
public class HomeWork01 {
    public static void main(String[] args) {

        PrintThread printThread = new PrintThread();
        ReadThread readThread = new ReadThread(printThread);
        printThread.start();
        readThread.start();

    }
}

class PrintThread extends Thread {
    private boolean loop = true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    @Override
    public void run() {
        while (loop) {
            System.out.println((int) (Math.random() * 100 + 1));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Print线程退出!!!");
    }
}

// 需要获取PrintThread
class ReadThread extends Thread {
    private PrintThread printThread;
    private Scanner sc = new Scanner(System.in);

    public ReadThread(PrintThread printThread) {
        this.printThread = printThread;
    }

    @Override
    public void run() {
        while (true) {
            // 接收用户输入
            System.out.println("请输入你的指令(Q 表示退出):");
            char key = sc.next().toUpperCase().charAt(0);// 获取输入的第一个字符并转成大写
            if ('Q' == key) {
                // 以通知的方式结束PrintThread线程
                printThread.setLoop(false);
                System.out.println("Read线程结束!!!");
                break;
            }
        }
    }
}

家庭作业2

如果将 syschronzied 同步代码块时有Thread.sleep() 导致互斥锁始终被第一个线程Thread-0占有,导致Thread-1超取

@Override
public void run() {
    while (true) {
        synchronized (this) {
            money -= 1000;
            System.out.println("用户:" + Thread.currentThread().getName() + " 取走1000" + ",余额=" + money);
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (money <= 0) {
            System.out.println("余额不足,结束取款!!!");
            break;
        }
    }
}

正确做法如下 synchronized 只修饰需要互斥的动作,不修饰进程的sleep 注:synchronized是非公平锁

/**
 *  作业02
 */
public class HomeWork02 {
    public static void main(String[] args) {
        GetMoney getMoney = new GetMoney();
        new Thread(getMoney).start();
        new Thread(getMoney).start();
    }
}


// 多线程共享资源 一般实现 Runnable接口
class GetMoney implements Runnable {
    private int money = 10000;

    @Override
    public void run() {
        while (true) {
            if (money <= 0) {
                System.out.println("用户:" + Thread.currentThread().getName() + " 余额不足,结束取款!!!");
                break;
            }
            synchronized (this) {
                money -= 1000;
                System.out.println("用户:" + Thread.currentThread().getName() + " 取走1000" + ",余额=" + money);
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

你可能感兴趣的:(java,jvm,面试)