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();
    }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容