为什么要使用多线程?

使用多线程的主要优点:

第一点:是提高CPU的利用率,比如程序需要执行一些等待的任务(用户读写文件等),这是就 凸显多线程的意义了,极大的提到了线程的利用率。

第二点:是提高应用程序的响应,尤其对图形化界面更有意义,可以提高用户的体验。

第三点:改变图形结构对长的线程复杂的线程分给多个线程,独立运行,有利于理解和修改。

怎样创建多线程:

创建多线程主要有以下几种方式:

第一种方式: 继承Thred类:

  1. 某一个类继承Thread类。
  2. 在子类中实现Thread类中的run()方法。我们线程的处理逻辑也是编辑在run方法中的。
  3. 在主启动类中进行创建实现类的对象并且调用start()方法,用于启动这个线程。

 实例:

public class shiyan1444  extends  Thread{
    @Override
    public void run() {

       for (int i=0;i<20;i++){
           System.out.println(i);
       }
    }
}

  class  mainTa{
      public static void main(String[] args) {
          shiyan1444 shiyan1444 = new shiyan1444();
          shiyan1444.start();
      }
  }

需要注意的点就是:每次创建一个线程就要重新实例化一个Thread类子类的对象,也就是相当于一个对象就是一个线程。

第二种方式:实现Runnable接口

  操作步骤:

  1. 实现类进行实现Runnable接口。
  2. 然后实现接口中的run()方法。在方法内写入线程的逻辑代码。
  3. 在启动类上面进行创建接口实现类的对象,并且实例化Thread类的对象,参数传入实现类的对象。
  4. 通过Thread对象来进行调用start()方法。

 实例:

public class shiyan1444  implements  Runnable{
    @Override
    public void run() {
        for (int i=0;i<20;i++){
            System.out.println(i);
        }
    }
}

class  mainTha{
    public static void main(String[] args) {
        shiyan1444 shiyan1444 = new shiyan1444();
        Thread thread = new Thread(shiyan1444);
        thread.start();
    }
}

需要注意的点就是:只需要创建一个Runnable实现类的对象即可,只需要创建Thread对象据可以实现多线程的创建

第三种方式:创建线程池

      提前创建好多个线程。提前放入线程之中,使用时直接获取 ,使用完返回池中,可以避免频繁的创建和销毁、实现重复利用。线程池的创建多线程是开发中最长用的方式。因为使用线程池有很多优点:提高响应速度、降低资源的消耗、便于线程的管理等。

public static void main(String[] args) {
    //提供指定线程数量的线程池
    ExecutorService executorService = Executors.newFixedThreadPool(10);
    //进行的强制转换   将ExecutorService 接口类型转换成他的实现类ThreadPoolExecutor 类型  
    ThreadPoolExecutor service= (ThreadPoolExecutor) executorService;
    service.setCorePoolSize();//设置线程池的属性
    System.out.println(executorService.getClass());//获取线程池的属性
    executorService.execute(new NumberT());//适合实现Runnadble接口时使用        提交
    executorService.submit(new Numberliz());//适合使用Callable使用             执行
    executorService.shutdown(); //关闭链接池
}

线程中常用的方法:

    在介绍线程安全和线程通信之前需要先介绍几个常用的方法:

  1. setName()和getName()方法分别是进行设置线程名称和获取线程名称。
  2. ThreadcurrentThread()是用来获取当前线程。
  3. sllep()方法是让当前线程进行休眠  参数输入具体的休眠时间。
  4. join()方法   举例:a线程调用了b线程的join()方法,那么a线程就会进入阻塞状态,直到b线程执行完毕。.
  5. yield() 方法,这个方法表示在对于一个正在被cpu处理的线程而讲 执行这个方法之后会进行释放cpu的资源,让其他的线程进行使用cpu()  不是以后cpu就不处理这个方法了   下一个时刻或许cpu就会再次执行这个这个线程。

线程安全问题:

       典型的生产者消费者问题,如果不进行处理线程安全问题的话,会出现 重复消费等等的问题。出现重复消费问题的原因是多个线程共享数据。

      解决思路:当一个线程操作共享数据的时候,其他的线程不能参与进来,当线程a操作完其他线程才可以继续操作 ,就算线程a 是处于阻塞状态其他线程是也要进行等待  。  所以我们使用同步机制(或者是说安全锁、同步监视器)来进行处理。

      那想要利用同步监视器来解决问题就要先知道什么是同步代码 同步代码块就是   共享数据: 多个线程共同操作的变量,比如 操作共享数据的代码,视为被同步的代码。

实例:

public class MyThread   extends   Thread {
    private  static    Integer   match=100;

@Override
    public void run() {
        while (true){
            synchronized(MyThread.class) {// 其中的锁对象要保持唯一性
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (match > 0) {
                    match--;
                    System.out.println(Thread.currentThread().getName() + "剩余:" + match);

                } else {
                    break;
                }
            }

        }

    }
}

class   sjdk{
    public static void main(String[] args) {
        MyThread myThread1 = new MyThread();
        myThread1.setName("窗口二");
        myThread1.start();
        MyThread myThread = new MyThread();
        myThread.setName("窗口一:");
        myThread.start();
    }

        

     需要注意的点就是在synchronized(MyThread.class)同步锁中的锁对象在多个线程的调用中必须保持唯一。

举例: 使用synchronized(this)是不可以的

      上述的例子是实现 Thread类的方式来创建的多线程,这样的话就需要我们在启动类中创建多个Thread类子类的对象来实现多线程的问题。既然是创建多个对象使用synchronized(this)当然是不可以的。

还需要特别注意的是 共享资源要设置成静态的,如果不设置静态的话,因为两个线程对象并不相同所以还是会出现重复消费的问题。

线程通讯; 

      以上述的生产者和消费者为例 ,即使解决了线程安全的问题,但是还是会出现某个线程出现大量消费,其它线程没有机会得到消费机会的问题。为了让线程之间轮流实现消费我们使用notify()方法和wait()方法来进行解决。

是进行唤醒线程操作   例如 :线程a调用这个方法 那么会释放其他线程的阻塞状态【只会唤醒一个线程,如果有多个线程的话只会唤醒优先级高的线程】

     notifyAll(), 和上一方法的区别是  它会唤醒所有的线程

     wait()        方法   会进行将执行这个方法的线程进行阻塞,并释放同步监视器

     

实例:

public class CommunicationTest   extends   Thread {
    private  static   Integer  id=100;
    @Override
    public void run() {
         while (true) {
             synchronized (CommunicationTest.class) {
                 CommunicationTest.class.notify();
                 if (id > 0) {
                     --id;
                     System.out.println(Thread.currentThread().getName() + id);
                     try {
                         CommunicationTest.class.wait();
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 } else {
                     break;
                 }
             }
         }
    }
}

class  mainT{

    public static void main(String[] args) {
        CommunicationTest communicationTest = new CommunicationTest();
        communicationTest.setName("窗口一:");
        communicationTest.start();
        CommunicationTest communicationTest1 = new CommunicationTest();
        communicationTest1.setName("窗口二:");
        communicationTest1.start();

    }

}

需要注意的点是:

    notify()和wait()方法只能作用在同步代码块或者是同步方法中,并且需要使用同步监视器中的锁对象来调用。