多线程

1. 什么是过程?
一个程序的运行期间就是过程,多个程序运行及时多过程。
多过程有什么作用?

多过程的作用不是进步执行速度,而是进步CPU的使用率。过程与过程之间是独立的。**什么是线程?**线程是过程的一个执行场景,一个过程能够启动多个线程。

多线程有什么作用
多线程不是为了进步执行速度,而是进步应用程序的使用率。
线程和线程共享“堆内存和办法区内存”,栈内存是独立的,意思就是一个线程一个栈。
会给人类一个错觉,认为多个线程是在同时并发运行。

多线程的创立与应用

多线程创立与应用有两种形式。

多线程创立的第一种形式:
应用分为三步

  • 定义线程,继承java.long.Thread类,重写run办法
  • 创立线程,new 类,调用Thread中的start()办法
  • 启动线程。

多线程创立的第二种形式
应用分为三步

  • 定义线程,履行Runable接口,实现run办法
  • 创立线程,调用Thread类中的构造方法,new Thread(Runable runable);调用start()办法
  • 运行线程

上代码
线程的第一种闯将形式:

@java    public class ThreadTest01{       public static void main(String[] args){          Thread thread = new Realize01();          thread.start();       }    }        class Realize01 exteads Thread{       public void run(){         for(int i = 0;i < 10;i++){           System.out.println("run-->" + i);         }      }       }

线程的第二种创立形式:

@java       public class ThreadTest02{          public static void main(String[] args){            Thread thread = new Thread(new Realize02);            thread.start();          }       }              class Realize02 implements Runable{            public void  run(){               for(int i = 0;i < 10;i++){                System.out.println("run-->" + i);            }          }         }

多线程的生命周期

  1. 新建:采纳new语句创立实现
  2. 就绪:执行start()之后示意就绪状态,就绪状态的线程示意有权力争取CPU的工夫片,谁先争取到工夫片,谁就先运行。
  3. 运行:占用了CPU的工夫片(就绪状态中线程或取到的工夫片取决于你运行的工夫,退出你在工夫片的范畴内没有把代码运行完,那么这个线程会从新到就绪状态,争取CPU的工夫片,再次进入到运行状态中会接着上次没有执行完的代码接着执行),执行run办法。
  4. 阻塞:执行了wait语句、执行了sleep语句和期待某个对象锁,期待输出的场景
  5. 完结:run办法完结。

线程的调度与管制

Java虚拟机要负责线程的调度,获得CPU的使用权,目前有两种调度模式;分时调度模式:所有线程轮流是用CPU的使用权,平均分配每个线程占用CPU的工夫片。 抢占试调度模式:优先让优先级高的线程应用CPU,如果线程的优先级雷同,那么会随机抉择一个,优先级高的CPU抢占的工夫片会绝对多一点。
线程的优先级:

  1. 线程的优先级分为三个

    • 最大的为10(MAX_PRIORITY )
    • 最小的为1(MIN_PRIORITY)
    • 默认的为5(NORM_PRIORITY)

线程的优先级取决于线程的执行程序

Thread.currentThread;获取线程以后的援用。
getName();获取以后的线程名称
setName();设置以后的线程名称
setPriority();设置线程优先级

           public class ThreadTest02 {          public static void <u>main(String[] </u><u>args</u><u>)</u> {              Thread t1 = new Realize();              //设置本线程的线程名字              t1.setName("t1");              //设置线程的优先级              t1.setPriority(6);              Thread t2 = new Realize();              t2.setName("t2");              t2.setPriority(7);                            t1.start();              t2.start();          }      }             class Realize extends Thread{            @Override            public void run() {                for (int i = 0; i < 10; i++) {                    System.out.println(Thread.currentThread().getName() + "-----" + i);                }            }        }

这行代码的运行后果t2的运行工夫是大于t1的,也就是t2的工夫片大于t1

线程的进行与中断
Thread.sleep(int time);在指定的毫秒数内让以后正在执行的线程休眠(暂停执行),此操作受到零碎计时器和调度程序精度和准确性的影响。
void join();期待该线程终止。在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程齐全执行毕当前,该线程才继续执行上来.
Thread.yield;暂停以后正在执行的线程对象,并执行其余线程。
void interrupt();字面意思是中断的意思,但在java中,是通过某种形式去告诉线程,而不是中断线程。

@java         public static void main(String[] args) throws Exception {           //字线程          Thread t1 = new Thread(new Runnable() {            @Override            public void run() {                for (int i = 0; i < 10; i++) {                    System.out.println(i);                }            }        });        t1.start();//        t1.join();//主线程的执行权让个t1        Thread.yield();//暂停主线程,执行t1线程        for (int i = 0; i < 10; i++) {            Thread.sleep(1000);            System.out.println("Hello Word!");        }    }

这里运次后果是t1线程执行,而后每隔一秒执行一次主线程。退出不把join给正文掉,线程的执行权就会让给t1。

当线程在睡眠期间如何被中断

@java      public class ThreadTest06 {          public static void main(String[] args) throws Exception {              Thread t2 = new Thread(new Runnable() {                  @Override                  public void run() {                      try {                          Thread.sleep(10000000000000l);                          System.out.println("Hello World!");                      } catch (InterruptedException e) {                          System.out.println("线程在睡眠中被吵醒了!");                          e.printStackTrace();                      }                      for (int i = 0; i < 5; i++) {                          System.out.println(i);                      }                  }              });              t2.start();              Thread.sleep(5000);              t2.interrupt();          }      }

如何正确的终止线程
每次循环的时候都判断一下a,如果是true就睡眠,如果是false就执行输入语句

@javapublic static void main(String[] args) throws Exception {        Realize07 t = new Realize07();        Thread tt = new Thread(t);        tt.start();        Thread.sleep(500);        t.a = false;    }}class Realize07 implements Runnable{    boolean a = true;    @Override    public void run() {        for (int i = 0; i < 10; i++) {            if (a){            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }else{            System.out.println("线程被中断了!");        }        }    }

线程的同步和锁机制
1. 异步线程

  • 两个线程同时执行,没有先后顺序,java个别都是异步执行,然而有时候在某些条件下,必须应用同步线 程。

2. 同步线程

  • 同步线程就是t1线程执行完而后执行t2线程
  • 执行条件就是必须在多线程环境下,两个线程共享同一块数据的时候,对这块数据进行删改操作。

上代码

 @java      /*       * <u>定义一个账户,对这个账户进行取钱。      * 定义两个线程同时进行操作      */      public class ThreadTest01  {          public static void main(String[] args) throws Exception{               Account act = new Account("account-01",5000);              Thread t1 = new Thread(new Realize(act));              Thread t2= new Thread(new Realize(act));              t1.start();              t2.start();          }      }      class Account{          private String name;//账户名          private double money;//账户余额          public Account(){          }          public Account(String name,double money){              this.name  = name ;              this.money = money;          }          public String getName() {              return name;          }          public void setName(String name) {              this.name = name;          }          public double getMoney() {              return money;          }          public void setMoney(double money) {              this.money = money;          }          /**           * 对账户进行取钱           */          public void drawmoney(double draw){              double nowmoney = this.money - draw;              this.setMoney(nowmoney);          }      }      class Realize implements Runnable{          Account act;          public Realize(){          }          public Realize(Account act){              this.act = act;          }          @Override          public void run() {              act.drawmoney(1000.0);              try{                Thread.sleep(1000);              }catch(Exception e){}              System.out.println(act.getName() + "取走1000元胜利,余额:"+act.getMoney());          }      }

运行后果:

@java   account-01取走1000元胜利,余额:4000.0   account-01取走1000元胜利,余额:4000.0   Process finished with exit code 0

两个线程同时拜访了一个操作,t1线程还没有执行完的时候,t2线程就曾经获取到余额是4000。t2不会等t1执行完在去执行。退出取款机是这样的操作,这是守法的。须要等t1线程执行完再去执行t2线程,其实同步线程有很多种办法,效率最高的就是synchronized。

3. synchronized(锁对象){执行的要同步的代码}

这里对run办法中公共执行的代码进行了加锁,谁先找到对象锁,那个线程就先执行,这两个线程的数据是共享的,所以对象锁只有一个。
synchronized也能够当做加载办法中。

这个默认的也是this。
两者的区别:

  • synchronized(锁对象){执行的要同步的代码}跟应用在办法中的成果一样,不过有些时候你无奈保障你run办法外面的代码都是公共的,所以一般来说,synchronized(){}效率更高。

当你拜访两个线程时,一个线程调用的办法加锁了,另一个线程调用的办法没有加锁,同时执行时,没有加锁的会不会受加锁的影响呢?

@java      public class ThreadTest02 {          public static void main(String[] args) throws Exception {             Realize02 rl = new Realize02();             T1 t = new T1(rl);             Thread t1 = new Thread(t);             t1.setName("t1");             Thread t2 = new Thread(t);             t2.setName("t2");             t1.start();             //保障T1先执行            Thread.sleep(1000);             t2.start();          }      }      class T1 implements Runnable{          Realize02 rl;          public T1(Realize02 rl) {              this.rl = rl;          }          public  T1(){          }          @Override          public void run() {              if (Thread.currentThread().getName().equals("t1")){                   rl.m1();              }              if (Thread.currentThread().getName().equals("t2")){                   rl.m2();              }          }      }       class Realize02{          public synchronized void m1(){              try {                  Thread.sleep(5000);              } catch (InterruptedException e) {                  e.printStackTrace();              }              System.out.println("m1.....");          }          public  void m2(){              System.out.println("m2.......");          }       }

运行后果:

@javam2.......m1.....

能够看到,只管在t1线程执行完让主线程休眠1s之后在再执行t2线程,然而m2办法并没有等m1办法休眠过后再执行,而是间接执行。
退出m2办法也加synchronize,那么m2会等m1先执行完后才执行,因为两个线程应用的是一个对象锁。谁先拿到谁就先执行。

@java     public synchronized void m2(){         System.out.println("m2......."); }

如果两个线程应用的不是同一个对象锁,那么m2办法同样不会受到m1办法的影响,在t2拿到就间接执行。

类锁

  • 如果你要增加的锁的办法不是成员办法,而是静态方法,那么这个锁指向的就不是对象锁而是 类锁
  • 类锁只有一个。
@java      public class ThreadTest02 {          public static void main(String[] args) throws Exception {             Realize02 rl1 = new Realize02();             Realize02 rl2 = new Realize02();             T1 t = new T1(rl1);             T1 tt = new T1(rl2);             Thread t1 = new Thread(t);             t1.setName("t1");             Thread t2 = new Thread(tt);             t2.setName("t2");             t1.start();             //保障T1先执行             Thread.sleep(1000);             t2.start();          }      }      class T1 implements Runnable{          Realize02 rl;          public T1(Realize02 rl) {              this.rl = rl;          }          public  T1(){          }          @Override          public void run() {              if (Thread.currentThread().getName().equals("t1")){                   <u>rl</u><u>.</u><u>m1</u><u>()</u>;              }              if (Thread.currentThread().getName().equals("t2")){                   <u>rl</u><u>.</u><u>m2</u><u>()</u>;              }          }      }       class Realize02{          public synchronized static void <u>m1()</u>{              try {                  Thread.sleep(5000);              } catch (InterruptedException e) {                  e.printStackTrace();              }              System.out.println("m1.....");          }           /**            *在m2也增加办法锁synchronize会等到m1执行完之后才执行,因为两个对象应用的是同一个对象锁            */          public synchronized static void <u>m2()</u>{              System.out.println("m2.......");          }       }

运行后果是,m2先等m1执行完而后才执行,只管两个应用的不是同一个对象。起因就是因为不论你是用的是不是同一个对象,只有是这个类的,那么类锁就只有一个,谁先拿到就执行谁。

死锁
当线程A锁住a的同时又想锁住b,线程B想锁住b的同时又想锁住a,这样“你要拿我,我要拿你就叫做死锁”

@java      public class ThreadTest03 {          public static void <u>main(String[] </u><u>args</u><u>)</u> {              Object o1 = new Object();              Object o2 = new Object();              Thread t1 = new Thread(new T2(o1,o2));              Thread t2 = new Thread(new T3(o1,o2));              t1.start();              t2.start();          }      }      class  <u>T2</u> implements Runnable{          Object o1;          Object o2;          public T2(Object o1, Object o2) {              this.o1 = o1;              this.o2 = o2;          }          @Override          public void run() {            synchronized (o1){                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                synchronized (o2){                }            }          }      }      class  T3 implements Runnable{          Object o1;          Object o2;          public T3(Object o1, Object o2) {              this.o1 = o1;              this.o2 = o2;          }          @Override          public void run() {              synchronized (o2){                  try {                      Thread.sleep(1000);                  } catch (InterruptedException e) {                      e.printStackTrace();                  }                  synchronized (o1){                  }              }          }      }

t2线程先锁住o1而后去锁o2,t3线程先锁住o2而后去锁o1。o1,o2为两个人的共享数据。

守护线程
java线程分为守护线程和非守护线程。非守护线程又叫做用户线程,咱们平时编写的一些线程就叫做用户线程。
守护线程会在所有的用户线程完结才会主动完结。
Java在运行的时候调动JVM,JVM是一个过程,外面有多个线程,JC(垃圾回收器)就是守护线程,它会在所有的用户过程完结完而后主动退出程序。
java吧线程设置守护线程的办法:在start之前调用strDaemon(true)办法。

  • setDaemon(true) 必须在 start() 之前设置,否则会抛出IllegalThreadStateException异样,该线程仍默认为用户线程,继续执行
  • 守护线程创立的线程也是守护线程
  • 守护线程不应该拜访、写入长久化资源,如文件、数据库,因为它会在任何工夫被进行,导致资源未开释、数据写入中断等问题
@java      public class TreadTest01 {          public static void main(String[] args) {              Thread t1 = new Thread(new on());              //设置该线程为守护线程              t1.setDaemon(true);              t1.start();              //主线程须要执行的代码              for (int i = 0; i < 10; i++) {                  System.out.println(Thread.currentThread().getName() + "----" + i);                  try {                      Thread.sleep(500);                  } catch (InterruptedException e) {                      e.printStackTrace();                  }              }                      }      }      class on implements Runnable{          @Override          public void run() {              int i = 0;              while(true){                  try {                      Thread.sleep(1000);                  } catch (InterruptedException e) {                      e.printStackTrace();                  }                                  System.out.println(Thread.currentThread().getName()+"----"+i);                  i++;              }          }         }

运行后果

main----0main----1main----2Thread-0----0main----3Thread-0----1main----4main----5Thread-0----2main----6main----7Thread-0----3main----8main----9Thread-0----4Process finished with exit code 0

只管t线程外面的执行的是死循环,然而应用了setDaeon()办法转换为守护线程之后,在所有的用户线程执行完之后,主动break(完结)。
定时器(Timer类)
Timer类的作用是设置打算工作,然而工作封装的类是TimerTask类。执行打算工作的代码要放入TimerTask的子类中,因而TimerTask是一个抽象类。
schedule(TimerTask task, Date time)办法 在指定的日期执行某一件事

@java      class Task02 extends <u>TimerTask</u>{          @Override          public void <u>run()</u> {              System.out.println("Just I miss you!");          }      }

实现类

@java      import java.text.SimpleDateFormat;      import java.util.Timer;      import java.util.TimerTask;      public class <u>TimerTest02</u> {          public static void main(String[] args) throws Exception {              Timer tmr = new Timer();              tmr.schedule(new <u>Task02</u>(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2020-10-6 13:04:00"));          }      }

运行后果

@javaJust I miss you!Process finished with exit code -1

schedule(TimerTask task,Date time,long period);激活从什么工夫开始,每隔多长时间,执行一次工作

@java      class Task extends <u>TimerTask</u> {          @Override          public void <u>run()</u> {             System.out.println(Calendar.getInstance().getTime().toLocaleString());          }      }

测试类

@java      import java.text.SimpleDateFormat;      import java.util.Calendar;      import java.util.Timer;      import java.util.TimerTask;      public class <u>TimerTest01</u> {          public static void main(String[] args) throws Exception {              //1.创立定时器              Timer tmr = new Timer();              tmr.schedule(new <u>Task</u>(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2020-10-6 13:07:00"),10*1000);          }      }

运行后果:每隔10秒钟就输入一次工夫!

@java 2020-10-6 13:07:022020-10-6 13:07:122020-10-6 13:07:22