Java实战开发篇-11 多线程

多线程

一、多线程中的几个概念

1.程序:静态的代码
2.进程:正在运行的一个程序 正在使用的QQ,Android Studio。进程用于管理所有的资源,不进行实际的任务
3.线程:完成具体任务,QQ运行起来就是线程(一个进程里面可以有多个线程)。运行QQ,聊天、视频、QQ游戏同时运行,这就是一个个线程
4.主线程:Java里面,main方法里面的代码就在主线程中运行。在手机里面,我们看到的主界面,就是一个主线程
5.子线程:除了主线程之外的线程

二、为什么使用多线程

在主线程里面,任务的执行是从上至下的,如果其中一个任务需要耗费大量时间。那么这个任务后面的任务就必须等这个任务结束后才能被执行,就会形成阻塞。这个时候就需要将这个任务放在另一个线程里面去执行(子线程)
*注:不管是主线程还是子线程都有自己独立的执行路径

三、如何开启一个线程

1.写一个类继承于Thread

步骤:
(1)创建类继承于Thread,具体执行的任务放在run()里面
(2)创建类的对象
(3)调用start()方法执行
注*线程的执行是通过抢占时间片来获取执行机会的,时间片是由操作系统来分配的
所以有多个线程的时候,每次执行的结果可能不一样

class TestThread extends Thread{
    //1.创建一个类继承于Thread
    //可以通过重写构造方法给子线程命名
    public TestThread(@NonNull String name) {
        super(name);
    }
    @Override
    //子类必须实现父类的run方法,这个线程执行的任务在run方法里面
    public void run() {
        System.out.println(getName());
        //也可以用Thread.currentThread(),获取当前线程的名字
        System.out.println("Hello World");
    }
}
public class MyClass {
    public static void main(String[] args){
         //2.创建具体的对象
        TestThread testThread = new TestThread("子线程");//给子线程命名
        //3.启动线程,不调用start无法启动线程
        testThread.start();
}
}
    public static void testRunnable(){
        //2.创建具体对象
        TestRunnable testRunnable = new TestRunnable();
        //3.创建一个Thread对象 让这个线程去执行testRunnable的任务
        Thread thread = new Thread(testRunnable);
        thread.start();
    }
}

2.写一个类实现Runnable接口

步骤:
(1)创建一个类实现Runnable接口,但它并不能分配线程
(2)创建一个该类的对象
(3)创建Thread类的对象来创建线程
(4)调用start方法开启线程

class TestRunnable implements Runnable{
    //1.创建一个类实现Runnable方法
    //这个类不能开启线程,也需要通过Thread来开启
    @Override
    public void run() {
        System.out.println("Hello World");
    }
}
public class MyClass {
    public static void main(String[] args){
        //2.创建具体对象
        TestRunnable testRunnable = new TestRunnable();
        //3.创建一个Thread对象 让这个线程去执行testRunnable的任务
        Thread thread = new Thread(testRunnable);
        thread.start();
}
}

3.两种启动方式的对比

第一种创建方法简单一些,但是无法实现多继承
第二种灵活性更强,因为接口可以实现多继承

四、线程的生命周期

线程的5种形态

new创建状态->start就绪状态-><-抢到时间片,运行状态(失去时间片进入就绪状态)->run死亡状态
运行状态中,可能遇到阻塞状态

1.创建状态

new Thread()

2.就绪状态

(1)调用start()
(2)阻塞条件结束
(3)正在运行的线程时间片被其他线程所抢夺

3.运行状态

从就绪状态到运行状态是由操作系统进行,外部无法干预

4.死亡状态

(1)run方法结束
(2)手动让线程暂停 stop(不建议使用,通过其他方式暂停)

5.阻塞状态

阻塞状态分为3种,同步阻塞synchronized,等待阻塞wait,其他阻塞sleep,join

五、如何让一个线程结束

(1)使用stop()方法;(此方法不建议使用)
(2)写一个变量来标识线程结束

class TestThread extends Thread{
    boolean stop = true;
    @Override
   public void run() {
        while (stop) {
   System.out.println("子线程");
    }
    }
   public void terminated(){
        stop = false;
        }//写一个终止方法
}
public class MyClass {
    public static void main(String[] args){
    TestThread t = new TestThread();
    t.start();
    for(int i = 0;i<20;i++){
        if(i==10){
            t.terminated();
    }
    }
}
}

六、线程礼让和线程插队

线程礼让:yield()
线程插队:join()
礼让的线程会直接进入就绪状态,如果这个线程再次获得时间片,它还会执行,所以可能礼让失败

TestRunnable testRunnable = new TestRunnable();
    Thread t1 = new Thread(testRunnable,"子线程1");
     t1.start();
     for(int i = 0;i<100;i++){
     System.out.println("主线程");
     if(i == 20){
     Thread.yield();
  }
  }
public class MyClass {
    public static void main(String[] args){
    TestThread t = new TestThread();
    t.start();
}
}

插队同理,插队后的线程执行完后再执行原线程

七、多线程的利弊

1.优点

(1)提高执行的效率
(2)不会阻塞主线程

2.缺点

(1)多个线程操作同一个资源时,有可能会出错

class BuyTickets extends Thread{
  static int total = 10;
    @Override
    public void run() {
       for(int i = 1;i<11;i++){
       if (total == 0) {
       stop();
    }
    }
       total--;
       System.out.println("第"+(10-total)+"张票购成功");
    }
    }
}
public class MyClass {
    public static void main(String[] args){
       BuyTickets Passenger1 = new BuyTickets();
       BuyTickets Passenger2 = new BuyTickets();
       BuyTickets Passenger3 = new BuyTickets();
       Passenger1.start();
       Passenger2.start();
       Passenger3.start();
    }
}

3.克服缺点的办法

(1)Lock锁,代码块必须使用同一把锁
(2)2.synchronized锁

class BuyTickets extends Thread{
    Object object = new Object();//创建一个临时对象
  static int total = 10;
    @Override
    public void run() {
      for(int i = 1;i<11;i++){
      synchronized (object) {
      if (total == 0) {
      stop();
    }
    }
      total--;
      System.out.println("第"+(10-total)+"张票购成功");
    }
    }
}
public class MyClass {
    public static void main(String[] args){
       BuyTickets Passenger1 = new BuyTickets();
       BuyTickets Passenger2 = new BuyTickets();
       BuyTickets Passenger3 = new BuyTickets();
       Passenger1.start();
       Passenger2.start();
       Passenger3.start();
    }
}

*注:锁的对象必须相同,每一个对象都维护一把锁
不管是锁代码块还是锁方法,尽量让锁的范围变小

八、线程间的通信

1.实现线程间通信的三个方法

(1)wait()让某个线程等待
(2)notify()唤醒某个线程
(3)notifyAll()唤醒所有线程
*注:这三个方法必须由同步监视器(必须被Lock或synchronized包装的代码块)来调用

public class MyClass {
    static intersection section = new intersection();
    public static void main(String[] args){
        new Thread(new Runnable() {
            @Override
            public void run() {
                section.printnum();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                section.printlet();
            }
        }).start();
    }
}//匿名对象和匿名内部类快速创建对象和调用方法
class intersection{
    int number = 1;
    char letter = 'a';
    int state = 1;//通过变化state的值来改变线程
    public synchronized void printnum(){
    while(true){
       if(state != 1){
       try {
       this.wait();
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
  }
    System.out.println(number);
    number++;
      if(number == 27){
      break;
  }
      state = 2;
      this.notify();
  }
  }
  public synchronized void printlet(){
        while(true){
        if(state != 2){
        try {
        this.wait();
   } catch (InterruptedException e) {
     e.printStackTrace();
   }
   }
    System.out.println(letter);
    letter++;
     if(letter == ('z'+1)){
     break;
    }
     state = 1;
     this.notify();
    }
    }
}

你可能感兴趣的:(Java实战开发篇-11 多线程)