多线程详解中(11.线程停止12.线程休眠sleep13.线程礼让yield14.线程强制执行_join15.观测线程状态16.线程的优先级17.守护线程18.线程同步机制19.三大不安全案例20.同步方法及同步块)

11.线程停止

1.线程状态

2.线程方法

3.线程停止

  • 不推荐使用JDK推荐的stop()、destroy()【已废弃】

  • 推荐线程自己停下来

  • 建议使用一个标志位进行终止变量当flag=false,则终止线程运行

//测试stop
//1.建议线程正常停止=======》利用次数,不建议死循环
//2.建议使用标志位=========》设置一个标志位
//3.不要使用stop()和destroy()等过时或JDK不建议使用的方法
public class TestStop implements Runnable{

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

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

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

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

       for (int i = 0; i < 1000; i++) {
           System.out.println("main"+i);
           if (i==900){
               //调用stop方法切换标志位,让线程停止
               testStop.stop();
               System.out.println("线程运行终止");
          }
      }
  }
}

12.线程休眠_sleep

  • 模拟倒计时

//模拟倒计时。。。
public class TextSleep2 {

   public static void main(String[] args) {
       try {
           tendown();
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
  }

   public  static void tendown() throws InterruptedException {
       int num = 10;

       while (true){
           Thread.sleep(1000);
           System.out.println(num--);
           if (num<=0){
               break;
          }
      }
  }
}

  • 打印系统当前时间

// 没有停止打印系统当前时间
public class TestSleep2_2 {
   public static void main(String[] args) {
       //打印系统当前时间
       Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间

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

   
}

  • 总结:

    • sleep(时间)指定当前线程阻塞的毫秒数

    • sleep存在异常InterruptedExce

    • sleep时间到达后线程进入就绪状态

    • sleep可以模拟网络延时,倒计时等

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

13.线程礼让_yield

  • 礼让线程,让当前正在执行的线程的线程暂停,但不阻塞

  • 将线程从运行状态转为就绪状态

  • 让CPU从新调度,礼让不一定成功!看CPU心情

//测试礼让线程
//礼让不一定成功,看CPU心情

public class TestYield {

   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()+"线程停止执行");
  }
}

14.线程强制执行_join

  • jion合并线程,待此线程执行完成后,在执行其他线程,其他线程阻塞

  • 可以想象成插队

//测试join方法想象为插队
public class TestJoin implements Runnable {

   @Override
   public void run() {
       for (int i = 0; i < 100; i++) {
           System.out.println("线程VIP来了"+i);
      }
  }

   public static void main(String[] args) throws InterruptedException {
       //启动我们的线程
       TestJoin testJoin = new TestJoin();
       Thread thread = new Thread(testJoin);
       thread.start();

       //主线程
       for (int i = 0; i < 1000; i++) {
           if(i==200){
               thread.join();//插队
          }
           System.out.println("main"+i);
      }


  }
}

15.观测线程状态

Thread.State

  • 线程状态,线程可以处于以下状态之一

    • NEW

      • 尚未启动的线程处于此状态

    • RUNNABLE

      • 在java虚拟机中执行的线程处于此状态

    • BLOCKED

      • 被阻塞等待监视器锁定的线程处于此状态

    • WAITING

      • 正在等待另一个线程执行特定动作的线程处于此状态

    • TIMED_WAITING

      • 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态

    • TERMINATED

      • 以退出的线程处于此状态

    一个线程可以再给定的时间点处于一个状态,这些状态是不反映任何操作系统线程状态的虚拟机状态

     

    //观察测试线程的状态
    public class TestState {
       public static void main(String[] args) throws InterruptedException {
           Thread thread = new Thread(()->{
               for (int i = 0; i < 5; i++) {
                   try {
                       Thread.sleep(100);
                  } 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){//判断只要线程不终止就一直执行下去
               Thread.sleep(100);
               state = thread.getState();//更新线程状态
               System.out.println(state);//输出线程的状态
          }
           
      }
    }

    //线程一旦进入死亡状态就不能再重新启动
    public class TestState {
       public static void main(String[] args) throws InterruptedException {
           Thread thread = new Thread(()->{
               for (int i = 0; i < 5; i++) {
                   try {
                       Thread.sleep(100);
                  } 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){//判断只要线程不终止就一直执行下去
               Thread.sleep(100);
               state = thread.getState();//更新线程状态
               System.out.println(state);//输出线程的状态
          }
           //从新启动线程
           thread.start();
      }
    }

16.线程的优先级

  • java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度那个线程来执行

  • 线程的优先级用数字来表示,范围从1~10

    • Thread.MIN_PRIORITY = 1;

    • Thread.MAX_PRIORITY = 10;

    • Thread.NORM_PRIORITY = 5;

  • 使用以下方式改变或获取优先级

    • getPriority().setPriority(int xxx)

  • 优先级的设置建议在start()调度前

  • 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度

//测试线程优先级
public class TestPriority {

   public static void main(String[] args) {
       //主线程默认优先级
       System.out.println(Thread.currentThread().getName()+"=========>"+Thread.currentThread().getPriority());
       Mypriority mypriority = new Mypriority();

       Thread t1 = new Thread(mypriority);
       Thread t2 = new Thread(mypriority);
       Thread t3 = new Thread(mypriority);
       Thread t4 = new Thread(mypriority);
       Thread t5 = new Thread(mypriority);
       Thread t6 = new Thread(mypriority);

       //先设置优先级,在启动
       t1.start();

       t2.setPriority(2);
       t2.start();

       t3.setPriority(4);
       t3.start();

       t4.setPriority(Thread.MAX_PRIORITY);//Thread.MAX_PRIORITY == 10
       t4.start();

       t5.setPriority(6);
       t5.start();

       t6.setPriority(Thread.MIN_PRIORITY);//Thread.MIN_PRIORITY == 1
       t6.start();
  }
}

class Mypriority implements Runnable{
   @Override
   public void run() {
       //Thread.currentThread().getName()             线程的名字
       //Thread.currentThread().getPriority()         线程的优先级
       System.out.println(Thread.currentThread().getName()+"=========>"+Thread.currentThread().getPriority());
  }
}
  • 一般优先级高的先调度

17.守护线程

  • 线程分为用户线程守护线程

  • 虚拟机必须确保用户线程执行完毕

  • 虚拟机不用等待守护线程执行完毕

  • 如,后台记录收入日志,监控内存,垃圾回收等待

//测试守护线程
//秦始皇守护你
public class TestDaemon {

   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 < 36500; i++) {
           System.out.println("你一生都开行的活着");
      }
       System.out.println("==========goodbyte! world======= ");
  }
}

虚拟机不用等待守护线程执行完毕

18.线程同步机制

  • 线程同步:多个线程操作同一个资源

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

  • 例子:

    • 现实生活中,我们会遇到“同一个资源多个人都想使用”的问题,比如,食堂排队打饭,每个人都想吃饭,最天然的解决办法办法就是,排队,一个个来

    • 处理多行程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程再使用

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

  • 由于同一进程的多个线程共享一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问的正确性,在访问时加入锁机制 synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可,存在问题:

    • 一个线程持有锁会导致其他所有需要此锁的线程挂起

    • 在多线程竞争下,加锁,释放锁会导致比较多上下文切换 和 调度延时,引起性能问题

    • 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题

19.三大不安全案例

  • 不安全的买票

//不安全的买票
public class UnsafeBuyTicket {
   public static void main(String[] args) {
       BuyTicket station = new BuyTicket();

       new Thread(station,"苦逼的我").start();
       new Thread(station,"牛逼的你们").start();
       new Thread(station,"可恶的黄牛党").start();
  }
}

class BuyTicket implements Runnable{

   //票
   private int ticketNums = 10;
   boolean flag = true;//外部停止方式

   @Override
   public void run() {
       //买票
       while (flag){
           try {
               buy();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
  }

   private void buy() throws InterruptedException {
       //判断是否有票
       if (ticketNums<=0){
           flag = false;
           return;
      }
       //模拟延时
       Thread.sleep(100);
       //买票
       System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
  }
}

  • 不安全的取钱

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

       Drawing you = new Drawing(account,50,"你");
       Drawing girlfriend = new Drawing(account,100,"girlfriend");

       you.start();
       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;

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

   //取钱
   @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);
       //this.getName() = Thread.currentThread().getName()
       //继承 Thread 类,Thread类里有getName()方法,getName()方法就是获得名字
       System.out.println(this.getName()+"手里的钱"+nowMoney);
  }
}

  • 不安全的集合

//线程不安全的集合
public class UnsafeList {
   public static void main(String[] args) {
       ArrayList<String> list = new ArrayList<>();
       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());
  }
}

20.同步方法及同步块(重点)

1同步方法

  • 由于我们可以通过Private关键字来保证数据对象只能方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种方法:synchronized方法和synchronized块

同步方法:public synchronized void method(int args){
   
}
  • synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须得调用该方法的对象的锁才能执行,否组线程会阻塞,方法一旦执行,就独占锁,直到该方法放回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

缺陷:若将一个大的方法声明为synchronized 将会影响效率

2同步方法的弊端

3同步块

  • 同步块:synchronized(Obj){}

  • Obj称之为同步监视器

    • Obj可以是任何对象,但是推荐共享资源作为同步监视器

    • 同步方法无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class[放射中讲]

  • 同步监视器的执行过程

    1. 第一个线程访问,锁定同步监视器,执行其中的代码

    2. 第二个线程访问,发现同步监视器被锁定,无法访问

    3. 第一个线程访问完毕,解锁同步监视器

    4. 第二个线程被访问,发现同步监视器没被锁,然后锁定并访问

4枷锁(三大不安全案例)

  • 买票

public class UnsafeBuyTicket {
   public static void main(String[] args) {
       BuyTicket station = new BuyTicket();

       new Thread(station,"苦逼的我").start();
       new Thread(station,"牛逼的你们").start();
       new Thread(station,"可恶的黄牛党").start();
  }
}

class BuyTicket implements Runnable{

   //票
   private int ticketNums = 10;
   boolean flag = true;//外部停止方式

   @Override
   public void run() {
       //买票
       while (flag){
           try {
               buy();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
  }

   //synchronized,同步方法
   private synchronized void buy() throws InterruptedException {
       //判断是否有票
       if (ticketNums<=0){
           flag = false;
           return;
      }
       //模拟延时
       Thread.sleep(100);
       //买票
       System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
  }
}

  • 取钱

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

       Drawing you = new Drawing(account,50,"你");
       Drawing girlfriend = new Drawing(account,100,"girlfriend");

       you.start();
       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;

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

   //取钱
   //synchronized 默认所的是this
   @Override
   public void run() {
       //锁的对象就是变化的量,需要增删改查的对象
       synchronized (account){
           //判断有没有钱
           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);
           //this.getName() = Thread.currentThread().getName()
           //继承 Thread 类,Thread类里有getName()方法,getName()方法就是获得名字
           System.out.println(this.getName()+"手里的钱"+nowMoney);
      }

  }
}

  • 集合

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

       ArrayList<String> list = new ArrayList<>();
       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());
  }
}

posted @ 2021-05-08 22:40  园小白  阅读(105)  评论(0编辑  收藏  举报