Thread多线程基础02

1. 线程状态

线程状态01

线程状态02

停止线程

  • 不推荐使用 JDK 提供的stop()、destroy()方法 【已废弃】
  • 推荐线程自己停止下来
  • 建议使用一个 标志位 进行终止变量。当flag = false,则终止线程运行
package com.Thread.state;


/*
测试stop类
1. 建议线程正常停止,--> 利用次数,不建议死循环
2. 建议使用标志位, ---> 设置一个标志位
3. 不要使用stop或者destroy等过时或者JDK已经不建议使用的方法
 */
public class TextStop implements Runnable {

    //1. 设置一个标志位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("run...Thread" + i++);
        }
    }

    //2.设置一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag = false;
    }

    public static void main(String[] args) {
        TextStop textStop = new TextStop();
        new Thread(textStop).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main....Thread" + i);
            if (i == 900){
               //调用stop方法切换标志位,让该线程停止 --> 停止的是实现Runnable接口类中重写的run()方法线程
                textStop.stop();
                System.out.println("该线程(其他)停止了");
            }
        }
    }
}

线程休眠_sleep

  • sleep(时间) 指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException;
  • sleep时间达到后线程进入就绪状态
  • sleep可以模拟网络延时,倒计时等
  • 每一个对象都有一个锁,sleep不会释放锁
package com.Thread.state;

//模拟网络延时:放大问题的发生性
public class TextSleep implements Runnable{

    private static  int tickets = 1;
    @Override
    public void run() {
        while (true){

            System.out.println(Thread.currentThread().getName() + "订购到了第" + tickets++ + "张车票");

            if (tickets > 10){
                break;
            }

            //模拟延时
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) {
        TextSleep tickets = new TextSleep();
        new Thread(tickets,"游客01").start();
        new Thread(tickets,"游客02").start();
        new Thread(tickets,"游客03").start();
    }
}
package com.Thread.state;

import javax.xml.crypto.Data;
import java.text.SimpleDateFormat;
import java.util.Date;

import static java.lang.System.currentTimeMillis;

public class TextSleep02 {

    public static void main(String[] args) {
        getCurrentTime();
    }

    //打印当前时间
    public static void getCurrentTime(){

        Date date = new Date(System.currentTimeMillis());   //获取当前系统时间

        while(true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
                date = new Date(System.currentTimeMillis());    //更新当前时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    //模拟倒计时
    public static void tenDown(int number) {
        while (true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(number--);
            if (number < 0) {
                System.out.println("生日快乐!");
                break;
            }
        }
    }
}

线程礼让_yield

线程礼让

package com.Thread.state;

public class TextYield {

    public static void main(String[] args) {

        MyYield myYield = new MyYield();
        new Thread(myYield,"A").start();
        new Thread(myYield,"B").start();
    }
}

class MyYield implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName() + "线程停止执行");
    }
}

线程强制执行_join

注意:start方法应该放到主线程的if方法里面,才能实现这个效果吧,不然就是同步执行

package com.Thread.state;

public class TextJoin {

    public static void main(String[] args) {

        MyJoin myJoin = new MyJoin();
        Thread thread = new Thread(myJoin);

        
        for (int i = 0; i < 500; i++) {
            System.out.println("main线程正在等待办理业务" + i);
            if (i == 200) {
                thread.start();
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }
}

class MyJoin implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("VIP线程来了,统统闪开!" + i);
        }
    }
}

观测线程状态

package com.Thread.state;

public class TextState {

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {

            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("///////////////////");
        });

        //观察状态
        Thread.State state = thread.getState();
        System.out.println(state);  //NEW

        //观察状态
        thread.start(); //启动线程
        state = thread.getState();
        System.out.println(state); //Run

        while (state != Thread.State.TERMINATED) { //只要线程不终止,就一直输出状态
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            state = thread.getState();  //更新线程状态
            System.out.println(state);

        }


    }
}

线程优先级

线程优先级

image-20240407144731054

package com.Thread.state;

//测试线程的优先级
public class TextPriority {
    public static void main(String[] args) {

        //主线程默认优先级 5
        System.out.println(Thread.currentThread().getName() + "--> " + Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();

        Thread thread01 = new Thread(myPriority);
        Thread thread02 = new Thread(myPriority);
        Thread thread03 = new Thread(myPriority);
        Thread thread04 = new Thread(myPriority);

        //先设置优先级,再启动
        thread01.setPriority(1);
        thread02.setPriority(2);
        thread03.setPriority(6);
        thread04.setPriority(10);   //Thread.MAX_PRIORITY = 10
        thread01.start();
        thread02.start();
        thread03.start();
        thread04.start();
    }
}

class MyPriority implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " ---> " + Thread.currentThread().getPriority());
    }
}

守护线程

  • 线程分为 用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
package com.Thread.state;

//测试守护线程
//上帝守护你
public class TextDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true); //默认是false 表示是用户线程,正常的线程都是用户线程

        thread.start(); //上帝,守护线程启动

        new Thread(you).start();    //你,用户线程启动
        
    }

}

//上帝
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("上帝保佑你");
        }
    }
}


//你
class You implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("你一生都开心的活着");
        }
        System.out.println("=====goodbye,the world=====");
    }
}

2. 线程同步

多个线程操作同一个资源

  • 并发:用一个对象被多个线程同时操作

image-20240407150926059

  • 每个对象都有一个锁,sleep不会释放锁

线程同步形成条件:队列 + 锁

  • 为了保证数据再方法中被访问时的正确性,再访问时加入 锁机制 synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。 存在以下问题:
    • 一个线程持有锁会导致其他所有需要此锁的线程挂起;
    • 再多线程竞争下,加锁,释放锁会导致比较多的 上下文切换 和 调度延时 ,引起性能问题
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

三大不安全案例

不安全买票

package com.Thread.syn;

//不安全的买票
//线程不安全  有负数
public class UnsafeBuyTickets {
    public static void main(String[] args) {
        ButTickets butTickets = new ButTickets();

        new Thread(butTickets,"小刘").start();
        new Thread(butTickets,"小王").start();
        new Thread(butTickets,"小何").start();
    }
}

class ButTickets implements Runnable{

    //票
    private int ticketNums = 10;
    boolean flag = true;    //外部停止方式
    @Override
    public void run() {
        //买票
        while (flag){
            buy();
        }
    }

    private void buy(){
        //判断是否有票
        if (ticketNums <= 0){
            flag = false;
            return;
        }
        //模拟延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //买票
        System.out.println(Thread.currentThread().getName() + "预定到了第" + ticketNums-- + "张票");
    }
}

不安全取钱

package com.Thread.syn;

//不安全的取钱
//两个人去银行取钱,账户
public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100,"结婚基金");

        Drawing drawing = new Drawing(account,50,"你");
        Drawing girlFriend = new Drawing(account,100,"girlFriend");

        drawing.start();
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        girlFriend.start();

    }
}

//账户
class Account {
    int money; //余额
    String name; //卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行:模拟取款
class Drawing extends Thread {
    Account account; //账户
    int drawingMoney; //取了多少钱
    int nowMoney; //现在手里有多少钱
//    String name;

    public Drawing(Account account,int drawingMoney,String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
//        this.name = name;

    }

    //取钱

    @Override
    public void run() {
        //判断有没有钱
        if (account.money - drawingMoney < 0) {
            System.out.println(Thread.currentThread().getName() + " : 余额不足");
            return;
        }

        //sleep可以放大问题的发生性
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //余额 = 开始卡内的钱 - 你取的钱
        account.money = account.money - drawingMoney;
        //你手里的钱
        nowMoney = nowMoney + drawingMoney;

        System.out.println(account.name + "余额为:" + account.money);
        //Thread.currentThread().getName() = this.getName()
        System.out.println(this.getName() + "手里的钱:" + nowMoney);
    }
}

不安全集合

package com.Thread.syn;

import java.util.ArrayList;
import java.util.List;

//线程不安全的集合
public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
         new Thread(() -> {
             list.add(Thread.currentThread().getName());
         }).start();
        }

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

同步方法及同步块

image-20240407172637526

锁默认的this方法或者类

方法或者类中有变化的值

package com.Thread.syn;

//不安全的买票
//线程不安全  有负数
public class UnsafeBuyTickets {
    public static void main(String[] args) {
        ButTickets butTickets = new ButTickets();

        new Thread(butTickets,"小刘").start();
        new Thread(butTickets,"小王").start();
        new Thread(butTickets,"小何").start();
    }
}

class ButTickets implements Runnable{

    //票
    private int ticketNums = 10;
    boolean flag = true;    //外部停止方式
    @Override
    public void run() {
        //买票
        while (flag){
            buy();
        }
    }

    private synchronized void buy(){
        //判断是否有票
        if (ticketNums <= 0){
            flag = false;
            return;
        }
        //模拟延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //买票
        System.out.println(Thread.currentThread().getName() + "预定到了第" + ticketNums-- + "张票");
    }
}

锁代码块

//锁的对象是变化的值,是需要增删改的对象
synchronized () {
    
}
package com.Thread.syn;

//不安全的买票
//线程不安全  有负数
public class UnsafeBuyTickets {
    public static void main(String[] args) {
        ButTickets butTickets = new ButTickets();

        new Thread(butTickets,"小刘").start();
        new Thread(butTickets,"小王").start();
        new Thread(butTickets,"小何").start();
    }
}

class ButTickets implements Runnable{

    //票
    private int ticketNums = 10;
    boolean flag = true;    //外部停止方式
    @Override
    public void run() {
        //买票
        while (flag){
            buy();
        }
    }

    private synchronized void buy(){
        //判断是否有票
        if (ticketNums <= 0){
            flag = false;
            return;
        }
        //模拟延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //买票
        System.out.println(Thread.currentThread().getName() + "预定到了第" + ticketNums-- + "张票");
    }
}

List线程不安全锁

package com.Thread.syn;

import java.util.ArrayList;
import java.util.List;

//线程不安全的集合
public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
         new Thread(() -> {
             synchronized (list) {
                 list.add(Thread.currentThread().getName());
             }
         }).start();
        }

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

CopyWriteArrayList 安全类型的集合

package com.Thread.syn;

import java.util.concurrent.CopyOnWriteArrayList;

//测试JUC 安全类型的集合
public class TextJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(list.size());
    }
}

死锁问题

image-20240407174702245

死锁问题

package com.Thread.syn;

//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {

    public static void main(String[] args) {
        Makeup makeup01 = new Makeup(0,"大姐");
        Makeup makeup02 = new Makeup(1,"二姐");

        makeup01.start();
        makeup02.start();

    }
}

//口红
class Lipstick {

}

//镜子
class Mirror {

}

class Makeup extends Thread {

    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice; //选择
    String girlName; //使用化妆品的人

    Makeup(int choice,String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        //化装
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {    //获得口号的锁
                System.out.println(this.girlName + "获得口红的锁");
                Thread.sleep(1000);

                synchronized (mirror) {  //一秒钟后获得镜子的锁
                    System.out.println(this.girlName + "获得镜子的锁");
                }
            }
        } else {
            synchronized (mirror) {    //获得镜子的锁
                System.out.println(this.girlName + "获得镜子的锁");
                Thread.sleep(2000);

                synchronized (lipstick) {  //一秒钟后获得的锁
                    System.out.println(this.girlName + "获得口红的锁");
                }
            }
        }
    }
}

解决死锁问题

package com.Thread.syn;

//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {

    public static void main(String[] args) {
        Makeup makeup01 = new Makeup(0,"大姐");
        Makeup makeup02 = new Makeup(1,"二姐");

        makeup01.start();
        makeup02.start();

    }
}

//口红
class Lipstick {

}

//镜子
class Mirror {

}

class Makeup extends Thread {

    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice; //选择
    String girlName; //使用化妆品的人

    Makeup(int choice,String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        //化装
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {    //获得口号的锁
                System.out.println(this.girlName + "获得口红的锁");
                Thread.sleep(1000);

            }
            synchronized (mirror) {  //一秒钟后获得镜子的锁
                System.out.println(this.girlName + "获得镜子的锁");
            }
        } else {
            synchronized (mirror) {    //获得镜子的锁
                System.out.println(this.girlName + "获得镜子的锁");
                Thread.sleep(2000);
            }

            synchronized (lipstick) {  //一秒钟后获得的锁
                System.out.println(this.girlName + "获得口红的锁");
            }
        }
    }
}

死锁避免方法

  • 产生死锁的四个必要条件
      1. 互斥条件:一个资源每次只能被一个进程使用
      2. 请求与保持条件:一个进程因请求资源而阻塞时,对以获得的资源保持不放
      3. 不剥夺条件:进程已经获得的资源,在未使用完之前,不能强行剥夺
      4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

只要想办法破解其中任意一个或者多个条件就可以避免死锁的发生

Lock锁

package com.Thread.seniro;

import java.util.concurrent.locks.ReentrantLock;

public class TextLock {
    public static void main(String[] args) {

        TextLock02 textLock02_01 = new TextLock02();
        new Thread(textLock02_01,"游客01").start();
        new Thread(textLock02_01,"游客02").start();
        new Thread(textLock02_01,"游客03").start();
    }
}

class TextLock02 implements Runnable {

    private static int tickets = 10;

    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();    //加锁
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "订到了第" + tickets-- + "张票");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    break;
                }
            }finally{
                lock.unlock();  //解锁
            }
        }
    }
}

synchronized 与 Lock 的对比

  • Lock是显示锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费较少的时间来调查线程,性能更好,并且有更好的扩展性(提供了跟多的子类)
  • 使用优先顺序
    • Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)
posted @ 2025-02-26 16:51  LYQ学Java  阅读(15)  评论(0)    收藏  举报