zoukankan      html  css  js  c++  java
  • Java基础篇---多线程

    内容导航:

    1、多线程的实现方式

    2、线程安全问题

    3、线程间通信

    4、生产者消费者模式

    第一部分多线程的实现方式

    在java中多线程实现方式有2种

    一、自定义一个类A,继承Thread类

    1 public class ThreadA extends Thread {
    2     public void run(){
    3        
    4         }
    5     }
    6 
    7 }

    此时ThreadA是一个线程类,在ThreadA中重写Thread类中的run方法

    调用方式如下

    ThreadA A = new ThreadA();
    A.start();

    二、自定义一个类B,实现Runable接口

    1 public class ThreadB implements Runnable {
    2     public void run() {
    3        
    4     }
    5 }

    此时ThreadB不是一个线程类,在ThreadB中重写Thread类中的run方法

    ThreadB b = new ThreadB();
    Thread t1 = new Thread(b);
    t1.start();

    需要在初始化Thread的时候,把Runnable接口的实现类的对象b作为参数传入


    第二部分是线程安全问题

    线程安全问题产生的场景:

     1 public class SaleTickets implements Runnable {
     2 
     3     private int num = 100;   //表示100张火车票   这是共享资源
     4 
     5     //买100张火车票
     6     public void run() {
     7         for (int i = 0; i < num; i++) {
     8             if (num > 0) {
     9                 System.out.println(Thread.currentThread().getName() + "买了编号为" + num + "的火车票");
    10                 num--;
    11                 try {
    12                     Thread.sleep(500);
    13                 } catch (InterruptedException e) {
    14                 }
    15             }
    16         }
    17     }
    18 }
    SaleTickets st=new SaleTickets ();
    Thread tA=new Thread(st);    //用户A
    Thread tB=new Thread(st);    //用户B
    
    tA.setName("用户A线程");
    tB.setName("用户B线程");
    
    tA.start();
    tB.start();
                

    进入12306购买火车票的时候,为了解决多个人同时进行抢票问题,需要使用多线程来增加系统效率,假如票共有100张,用户A和用户B同时进行买票,如果是单线程,每次用户买票的时候,首先会打印用户买到了哪张票,然后票的总数num减1。

    但是由于是多线程,多线程有一个特性就是线程的执行顺序由cpu来分配,所以执行顺序是不确定的,假如用户A执行SaleTickets方法中的第10行的时候,会打印出用户A线程买了编号为100的火车票,此时用户B线程也开始执行SaleTickets方法,用户B也会打印出用户B线程买了编号为100的火车票

    这种结果不是合理的,所以就引发线程安全问题

    解决线程安全问题的方式有2种

    1、同步代码块

     1 public class SaleTickets implements Runnable {
     2 
     3     private int num = 100;   //表示100张火车票   这是共享资源
     4 
     5     //买100张火车票
     6     public void run() {
     7         for (int i = 0; i < num; i++) {
     8             synchronized (this){
     9                 if (num > 0) {
    10                 System.out.println(Thread.currentThread().getName() + "买了编号为" + num + "的火车票");
    11                 num--;
    12                 try {
    13                     Thread.sleep(500);
    14                 } catch (InterruptedException e) {
    15                 }
    16              }
    17              }   
    18         }
    19     }
    20 }            

    2、同步方法

     1 public class SaleTickets implements Runnable {
     2 
     3     private int num = 100;   //表示100张火车票   这是共享资源
     4 
     5     public synchronized  void saleOne(){
     6         if (num > 0) {
     7             System.out.println(Thread.currentThread().getName() + "买了编号为" + num + "的火车票");
     8             num--;
     9             try {
    10                 Thread.sleep(500);
    11             } catch (InterruptedException e) {
    12             }
    13         }
    14     }
    15 
    16     //买100张火车票
    17     public void run() {
    18         for (int i = 0; i < 10; i++) {
    19             saleOne();
    20         }
    21     }
    22 }

    此时同步方法中的synchronized的锁默认为this


    第三部分是线程间通信

    线程间的通信机制为等待唤醒机制,多个线程通信的前提是共用同一把锁 

    wait(long  timeout) 当前线程释放锁,并等待timeout毫秒

    notify 唤醒持有同一锁的某个线程

    1、定义一个静态object对象,作为线程间通信的锁

    1 public class MyLock {
    2     public static Object obj = new Object();
    3 }

    2、定义线程A

     1 //定义一个线程类  输出1
     2 public class ThreadA extends Thread {
     3 
     4     public void run(){
     5         for(int j=0;j<10;j++){
     6             synchronized (MyLock.obj) {
     7                 System.out.println(1);
     8                 MyLock.obj.notify();
     9                 try {
    10                     MyLock.obj.wait();
    11                 } catch (InterruptedException e) {
    12                     e.printStackTrace();
    13                 }
    14             }
    15         }
    16     }
    17 
    18 }

    3、定义线程B

     1 //定义一个线程类   输出2
     2 public class ThreadB extends Thread {
     3 
     4     public void run(){
     5         for(int j=0;j<10;j++){
     6             synchronized (MyLock.obj) {
     7                 System.out.println(2);
     8                 MyLock.obj.notify();
     9                 try {
    10                     MyLock.obj.wait();
    11                 } catch (InterruptedException e) {
    12                     e.printStackTrace();
    13                 }
    14             }
    15         }
    16     }
    17 
    18 }

    4、定义主函数执行线程A和线程B

    1 public class TestThreadForSignal {
    2     public static void main(String[] args){
    3         new ThreadA().start();
    4         new ThreadB().start();
    5     }
    6 }

    执行结果:交替打印1和2

    执行原理:先执行线程A,会被同步代码块synchronized锁住,打印1,然后唤醒共用MyLock.obj锁的线程B,然后线程A进入等待,并释放锁。然后执行线程B,会被同步代码块synchronized锁住,打印2,然后唤醒共用MyLock.obj锁的线程A,然后线程B进入等待,并释放锁。接下来继续执行线程A循环往复.......


    第四部分是生产者消费者模式

    利用线程间通信可以很好的实现生产者消费者模式

    因为这个代码跟上面线程通信的代码很类似,这里博主不打算继续码代码了,用文字做一个简要说明

    线程A (农夫)  往篮子里放苹果   如果篮子满了(苹果数量=10),线程A wait,如果没满(苹果数量<10),往篮子里放苹果,同时告诉B(小孩儿) notify唤醒

    线程B (小孩儿)从篮子里拿苹果吃   如果篮子空了(苹果数量=0),线程B wait   如果篮子里还有苹果(苹果数量>0),从篮子里拿苹果吃   同时告诉A(农夫)notify唤醒

    在这个场景里线程A(农夫)是生产者,线程B(小孩儿)是消费者

  • 相关阅读:
    【重温设计模式】之005单例模式
    【重温设计模式】之004抽象工厂模式
    【重温设计模式】之003工厂方法模式
    【重温设计模式】之002简单工厂模式
    【重温设计模式】之001概述
    【工具】之003-Windows下常用工具
    【工具】之002-Mac下常用工具
    【工具】之001-CentOS7 最小化安装配置
    做人要精致,做事要靠谱
    Vue+Vue-router微信分享功能
  • 原文地址:https://www.cnblogs.com/baixiaoguang/p/11912953.html
Copyright © 2011-2022 走看看