• 线程


    基本概念

    进程: 进程就是正在运行的应用程序。 进程了负责了内存空间划分。
    线程: 一个进程中的 代码是由线程去执行的,线程也就是进程中一个执行路径。
    多线程: 一个进程中有多个线程可以同时执行任务。

    注意:任何一个java程序中至少存在两个线程:main线程和垃圾回收线程。

    多线程的好处于弊端
    多线程 的好处:
        1. 解决一个进程中可以同时执行多个任务的问题。
        2. 提高了资源利用率。
    多线程的弊端:
        1. 增加了cpu的负担。
        2. 降低了一个进程中线程 的执行概率。
        3. 出现了线程 安全问题。
        4. 会引发死锁现象。

    线程的声明周期

    创建线程的方式

    方式一 : 继承Thread类
            1. 自定义一个类继承Thread类。
            2. 重写Thread类的run方法,把自定义线程的任务代码写在run方法上。
            3. 创建Thread的子类对象,并且调用start方法启动一个线程。

    注意:千万不要直接调用run方法,调用start方法的时候线程就会开启,线程一旦开启就会执行run方法中代码,如果直接调用run方法,那么就 相当于调用了一个普通的方法而已。

    代码示例:

     1 //模拟QQ边聊天边视频的场景
     2 class TalkThread extends Thread{
     3     @Override
     4     public void run() {
     5         while (true) {
     6             System.out.println("正在聊天!!!!");
     7         }
     8     }
     9 }
    10 
    11 class VideoThread extends Thread{
    12     @Override
    13     public void run() {
    14         while (true) {
    15             System.out.println("正在视频!!!!");
    16         }
    17     }
    18 }
    19 
    20 public class TestThread {
    21     public static void main(String[] args) {
    22         TalkThread talkThread = new TalkThread();
    23         talkThread.start();
    24         VideoThread videoThread = new VideoThread();
    25         videoThread.start();
    26     }
    View Code

    方式二:实现Runnable接口
        1. 自定义一个类实现Runnable接口。
        2. 实现Runnable接口 的run方法,把自定义线程的任务定义在run方法上。
        3. 创建Runnable实现类对象。
        4. 创建Thread类的对象,并且把Runnable实现类的对象作为实参传递。
        5. 调用Thread对象的start方法开启一个线程。

    注意:Runnable实现类的对象并 不是一个线程对象,只不过是实现了Runnable接口 的对象而已。只有是Thread或者是Thread的子类才是线程 对象。

     1 //模拟QQ边聊天边视频的场景
     2 class Talk implements Runnable{
     3     public void run() {
     4         while (true) {
     5             System.out.println("正在聊天!!!!");
     6         }
     7     }
     8 }
     9 
    10 class Video implements Runnable{
    11     public void run() {
    12         while (true) {
    13             System.out.println("正在视频!!!!");
    14         }
    15     }
    16 }
    17 
    18 public class TestThread {
    19     public static void main(String[] args) {
    20         Talk talk = new Talk();
    21         Thread talkThread = new Thread(talk);
    22         talkThread.start();
    23         Video video = new Video();
    24         Thread videoThread = new Thread(video);
    25         videoThread.start();
    26     }
    27 }
    View Code

    常用方法

      Thread(String name)     初始化线程的名字
          setName(String name)    设置线程对象名
      getName()             返回线程的名字
      sleep()                 线程睡眠指定的毫秒数。 静态的方法, 那个线程执行了sleep方法代码那么就是那个线程睡眠。
      currentThread()      返回当前的线程对象,该方法是一个静态的方法, 注意: 哪个线程执行了currentThread()代码就返回那个线程的对象。         getPriority()             返回当前线程对象的优先级   默认线程的优先级是5
      setPriority(int newPriority) 设置线程的优先级    虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现(最大的优先级是10 ,最小的1 , 默认是5)。

    安全问题

    出现线程安全问题的根本原因:
        1. 存在两个或者两个以上 的线程对象,而且线程之间共享着一个资源。
        2. 有多个语句操作了共享资源。

    线程安全问题的解决方案:sun提供了线程同步机制让我们解决这类问题的。

    方式一:同步代码块

      同步代码块的格式:
                    synchronized(锁对象){
                        需要被同步的代码...
                    }

      同步代码块要注意事项:
            1. 任意的一个对象都可以做为锁对象。
            2. 在同步代码块中调用了sleep方法并不是释放锁对象的。
            3. 只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率的。
            4. 多线程操作的锁对象必须是唯一共享的。否则无效。

    代码示例如下:

     1 //模拟3个窗口同时在售50张 票 。
     2 class SellTacketThread extends Thread{
     3     static int num = 50;
     4     public SellTacketThread(String name) {
     5         super(name);
     6     }
     7     @Override
     8     public void run() {
     9         while (true) {
    10             synchronized ("同步锁") {
    11                 if (num>0) {
    12                     System.out.println(Thread.currentThread().getName()+"售出了弟"+num+"张票");
    13                     num--;
    14                 }else{
    15                     System.out.println("售罄了..");
    16                     break;
    17                 }
    18             }
    19         }
    20     }
    21 }
    22 
    23 public class TestThread {
    24     public static void main(String[] args) {
    25         SellTacketThread seller1 = new SellTacketThread("窗口1");
    26         SellTacketThread seller2 = new SellTacketThread("窗口2");
    27         SellTacketThread seller3 = new SellTacketThread("窗口3");
    28         seller1.start();
    29         seller2.start();
    30         seller3.start();
    31     }
    32 }
    View Code

    方式二:同步函数

      所谓同步函数就是使用synchronized修饰一个函数。

    同步函数要注意的事项 :
            1. 如果是一个非静态的同步函数的锁对象是this对象,如果是静态的同步函数的锁 对象是当前函数所属的类的字节码文件(class对象)。
            2. 同步函数的锁对象是固定的,不能由随意指定的。

    代码示例:

     1 //模拟3个窗口同时在售50张 票 。
     2 class SellTacketThread extends Thread{
     3     static int num = 50;
     4     public SellTacketThread(String name) {
     5         super(name);
     6     }
     7     @Override
     8     public synchronized void run() {
     9         sellTacket();
    10     }
    11     public synchronized void sellTacket(){
    12         while (true) {
    13             synchronized ("同步锁") {
    14                 if (num>0) {
    15                     System.out.println(Thread.currentThread().getName()+"售出了弟"+num+"张票");
    16                     num--;
    17                 }else{
    18                     System.out.println("售罄了..");
    19                     break;
    20                 }
    21             }
    22         }
    23     }
    24 }
    25 
    26 public class TestThread {
    27     public static void main(String[] args) {
    28         SellTacketThread seller1 = new SellTacketThread("窗口1");
    29         SellTacketThread seller2 = new SellTacketThread("窗口2");
    30         SellTacketThread seller3 = new SellTacketThread("窗口3");
    31         seller1.start();
    32         seller2.start();
    33         seller3.start();
    34     }
    35 }
    View Code

    推荐使用: 同步代码块。
            原因:
                1. 同步代码块的锁对象可以由我们随意指定,方便控制。同步函数的锁对象是固定 的,不能由我们来指定。
                2. 同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数的所有代码都被同步了。

    死锁问题

    java中同步机制解决了线程安全问题,但是也同时引发死锁现象。
    死锁现象出现 的根本原因:
        1. 存在两个或者两个以上的线程。
        2. 存在两个或者两个以上的共享资源。
    死锁现象的解决方案: 没有方案。只能尽量避免发生而已。

    代码示例:

     1 class DeadLock extends Thread{
     2     
     3     public DeadLock(String name){
     4         super(name);
     5     }
     6     
     7     public void run() {
     8         if("张三".equals(Thread.currentThread().getName())){
     9             synchronized ("遥控器") {
    10                 System.out.println("张三拿到了遥控器,准备 去拿电池!!");
    11                 synchronized ("电池") {
    12                     System.out.println("张三拿到了遥控器与电池了,开着空调爽歪歪的吹着...");
    13                 }
    14             }
    15         }else if("李四".equals(Thread.currentThread().getName())){
    16             synchronized ("电池") { 
    17                 System.out.println("狗娃拿到了电池,准备去拿遥控器!!");
    18                 synchronized ("遥控器") {
    19                     System.out.println("狗娃拿到了遥控器与电池了,开着空调爽歪歪的吹着...");
    20                 }
    21             }
    22         }    
    23     }
    24 }
    25 
    26 public class Demo2 {
    27 
    28     public static void main(String[] args) {
    29         DeadLock thread1 = new DeadLock("张三");
    30         DeadLock thread2 = new DeadLock("李四");
    31         //开启线程
    32         thread1.start();
    33         thread2.start();
    34     }
    35 }
    View Code

    线程通信

    线程通讯: 一个线程完成了自己的任务时,要通知另外一个线程去完成另外一个任务.

    相关函数:

      wait():  等待   如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify方法才能唤醒。
      notify(): 唤醒    唤醒线程池等待线程其中的一个。
      notifyAll() : 唤醒线程池所有等待线程。

    wait与notify方法要注意的事项:
        1. wait方法与notify方法是属于Object对象 的。
        2. wait方法与notify方法必须要在同步代码块或者是同步函数中才能 使用。
        3. wait方法与notify方法必需要由锁对象调用。

    看一个经典的例子:生产者和消费者

    代码如下:

     1 //产品类
     2 class Product{
     3     String name;  //名字
     4     double price;  //价格
     5     boolean flag = false; //产品是否生产完毕的标识,默认情况是没有生产完成。
     6 }
     7 
     8 //生产者
     9 class Producer extends Thread{
    10     Product  p ;      //产品
    11     public Producer(Product p) {
    12         this.p  = p ;
    13     }
    14     @Override
    15     public void run() {
    16         int i = 0 ; 
    17         while(true){
    18          synchronized (p) {
    19             if(p.flag==false){
    20                  if(i%2==0){
    21                      p.name = "苹果";
    22                      p.price = 6.5;
    23                  }else{
    24                      p.name="香蕉";
    25                      p.price = 2.0;
    26                  }
    27                  System.out.println("生产者生产出了:"+ p.name+" 价格是:"+ p.price);
    28                  p.flag = true;
    29                  i++;
    30                  p.notify(); //唤醒消费者去消费
    31             }else{
    32                 //已经生产 完毕,等待消费者先去消费
    33                 try {
    34                     p.wait();   //生产者等待
    35                 } catch (InterruptedException e) {
    36                     e.printStackTrace();
    37                 }
    38             }             
    39         }    
    40       }    
    41     }
    42 }
    43 
    44 
    45 //消费者
    46 class Customer extends Thread{
    47     Product p; 
    48     public  Customer(Product p) {
    49         this.p = p;
    50     }
    51     
    52     @Override
    53     public void run() {
    54         while(true){
    55             synchronized (p) {    
    56                 if(p.flag==true){  //产品已经生产完毕
    57                     System.out.println("消费者消费了"+p.name+" 价格:"+ p.price);
    58                     p.flag = false; 
    59                     p.notify(); // 唤醒生产者去生产
    60                 }else{
    61                     //产品还没有生产,应该 等待生产者先生产。
    62                     try {
    63                         p.wait(); //消费者也等待了...
    64                     } catch (InterruptedException e) {
    65                         e.printStackTrace();
    66                     }
    67                 }
    68             }
    69         }    
    70     }
    71 }
    72 
    73 public class Demo5 {
    74     
    75     public static void main(String[] args) {
    76         Product p = new Product();  //产品
    77         //创建生产对象
    78         Producer producer = new Producer(p);
    79         //创建消费者
    80         Customer customer = new Customer(p);
    81         //调用start方法开启线程
    82         producer.start();
    83         customer.start();
    84     }
    85 }
    View Code

    线程的停止

     线程的停止:
         1. 停止一个线程我们一般都会通过一个变量去控制的。
         2. 如果需要停止一个处于等待状态下的线程,那么我们需要通过变量配合notify方法或者interrupt()来使用。

    代码如下:

     1 public class Demo6 extends Thread {
     2     
     3     boolean flag = true;
     4     
     5     public Demo6(String name){
     6         super(name);
     7     }
     8     
     9     
    10     @Override
    11     public synchronized void run() {
    12         int i = 0 ;
    13         while(flag){
    14             try {
    15                 this.wait(); //狗娃等待..
    16             
    17             } catch (InterruptedException e) {
    18                 System.out.println("接收到了异常了....");
    19             }
    20             System.out.println(Thread.currentThread().getName()+":"+i);
    21             i++;
    22         }
    23     }
    24     public static void main(String[] args) {
    25         Demo6 d = new Demo6("狗娃");
    26         d.setPriority(10);
    27         d.start();
    28         for(int i = 0 ; i<100 ; i++){
    29             System.out.println(Thread.currentThread().getName()+":"+i);
    30             if(i==80){
    31                 d.flag = false;
    32                 d.interrupt(); //把线程的等待状态强制清除,被清除状态的线程会接收到一个InterruptedException。 
    33                 /*synchronized (d) {                    
    34                     d.notify();
    35                 }*/
    36             }
    37         }
    38     }
    39 
    40 }
    View Code

     守护线程(后台线程)

     一个线程默认都不是守护线程。需要使用setDaemon(boolean b)来设置。

    注意:在一个进程中如果只剩下了守护线程,那么守护线程也会死亡。

     代码如下:

     1 public class Demo7 extends Thread {
     2     
     3     public Demo7(String name){
     4         super(name);
     5     }
     6     
     7     @Override
     8     public void run() {
     9         for(int i = 1 ; i<=100 ; i++){
    10             System.out.println("更新包目前下载"+i+"%");
    11             if(i==100){
    12                 System.out.println("更新包下载完毕,准备安装..");
    13             }
    14             try {
    15                 Thread.sleep(100);
    16             } catch (InterruptedException e) {
    17                 e.printStackTrace();
    18             }
    19         }
    20     }
    21     
    22     public static void main(String[] args) {
    23          Demo7 d = new Demo7("后台线程");
    24          d.setDaemon(true); //setDaemon() 设置线程是否为守护线程,true为守护线程, false为非守护线程。
    25         // System.out.println("是守护线程吗?"+ d.isDaemon()); //判断线程是否为守护线程。
    26          d.start();
    27          
    28          for(int i = 1 ; i<=100 ; i++){
    29              System.out.println(Thread.currentThread().getName()+":"+i);
    30          }
    31          
    32     }
    33 
    34 }
    View Code

    join方法

      在现有线程的基础上加入新的线程,并且现有线程必须让步于新的线程先完成任务, 才能继续执行。

    代码示例如下:

     1 //老妈
     2 class  Mon extends Thread{
     3     
     4     public void run() {
     5         System.out.println("妈妈洗菜");
     6         System.out.println("妈妈切菜");
     7         System.out.println("妈妈准备炒菜,发现没有酱油了..");
     8         //叫儿子去打酱油
     9         Son s= new Son();
    10         s.start();
    11         try {
    12             s.join();  //加入。 一个线程如果执行join语句,那么就有新的线程加入,执行该语句的线程必须要让步给新加入的线程先完成任务,然后才能继续执行。
    13         } catch (InterruptedException e) {
    14             e.printStackTrace();
    15         }
    16         System.out.println("妈妈继续炒菜");
    17         System.out.println("全家一起吃饭..");        
    18     }
    19 } 
    20 
    21 class Son extends Thread{
    22     
    23     @Override
    24     public void run() {
    25         System.out.println("儿子下楼..");
    26         try {
    27             Thread.sleep(1000);
    28         } catch (InterruptedException e) {
    29             e.printStackTrace();
    30         }
    31         System.out.println("儿子一直往前走");
    32         System.out.println("儿子打完酱油了");
    33         System.out.println("上楼,把酱油给老妈");
    34     }
    35 }
    36 
    37 public class Demo8 {
    38     
    39     public static void main(String[] args) {
    40         Mon m = new Mon();
    41         m.start();
    42     }
    43 }
    View Code
  • 相关阅读:
    Android自动化测试框架UIAutomator原理浅析
    UiAutomator和Appium之间的区别2
    UiAutomator、UiAutomator2、Bootstrap的关系
    好的博客和网站
    appium介绍和工作原理
    UiAutomator1.0 与 UiAutomator2.0
    Jenkins之配置GitHub-Webhook2
    jenkins部署github项目持续集成
    Windows下安装的Jenkins修改默认端口号8080(修改配置文件的方式)
    Git使用教程
  • 原文地址:https://www.cnblogs.com/nicker/p/6225407.html
Copyright © 2020-2023  润新知