zoukankan      html  css  js  c++  java
  • Java笔记(十九)……多线程

    概述

    进程:

    是一个正在执行中的程序

    每一个进程执行都有一个执行顺序,该执行顺序是一个执行路径,或者叫一个控制单元

    线程:

    就是进程中的一个独立的控制单元,线程在控制着进程的执行

    一个进程中至少有一个线程

    Java JVM启动的时候会有一个进程java.exe,该进程中至少有一个线程负责Java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称之为主线程,与C类似,java.exe相当于所有进程的父进程,而且JVM启动了不止一个线程,还有负责垃圾回收的线程

    创建线程

    第一种创建方式:继承Thread类

       1: class Sell extends Thread
       2: {
       3:     private int tickets = 100;
       4:     Sell()
       5:     {
       6:         //启动线程
       7:         start();
       8:     }
       9:     //复写Thread类中的run方法,将线程运行所需的代码写到run方法中
      10:     public void run()
      11:     {
      12:         while(tickets > 0)
      13:         {
      14:             System.out.println("tickets = "+tickets--);
      15:         }
      16:     }
      17: }

    第二种创建方式:实现Runnable接口

       1: class Sell implements Runnable
       2: {
       3:     private int tickets = 100;
       4:     
       5:     //实现Runnable接口的run方法
       6:     public void run()
       7:     {
       8:         while(tickets > 0)
       9:         {
      10:             System.out.println("tickets = "+tickets--);
      11:         }
      12:     }
      13: }
      14: class ThreadDemo 
      15: {
      16:     public static void main(String[] args) 
      17:     {
      18:         //将Runnable接口的子类Sell传递给Thread的构造函数,并启动线程
      19:         new Thread(new Sell()).start();
      20:     }
      21: }

    实现方式与继承方式的区别

    其实Thread类同样也是实现了Runnable接口,所以我们要的只是run方法,那么我们只要实现Runnable接口即可,这样避免了单继承的局限性,我们在实现Runnable接口的同时还可以继承其他的类,扩展了功能

    线程权限问题

    thread1

    线程安全问题--同步

    当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误

       1:  
       2: class Sell implements Runnable
       3: {
       4:     private int tickets = 10;
       5:     
       6:     //实现Runnable接口的run方法
       7:     public void run() 
       8:     {
       9:         while(tickets > 0)
      10:         {
      11:             try{Thread.sleep(500);}catch(Exception e){}
      12:             System.out.println(Thread.currentThread().getName()+"..tickets = "+(--tickets));
      13:         }
      14:     }
      15: }

    thread3

    当我们让线程操作共享数据时,暂停一会,可以发现,每个线程很容易-1剩余票数的错误

    解决方法

    对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不能参与进来执行

    Java对多线程的安全问题提供了专业的解决方式,就是同步代码块或者同步函数

    synchronized(对象)

    {

    需要被同步的代码

    }

    synchronized void show()

    {

    函数内容

    }

       1: class Sell implements Runnable
       2: {
       3:     private int tickets = 10;
       4:     
       5:     //实现Runnable接口的run方法
       6:     public void run() 
       7:     {
       8:         //加入锁,使代码同步
       9:         synchronized(this)
      10:         {
      11:             while(tickets > 0)
      12:             {
      13:                 try{Thread.sleep(500);}catch(Exception e){}
      14:                 System.out.println(Thread.currentThread().getName()+"..tickets = "+(--tickets));
      15:             }
      16:         }
      17:     }
      18: }

    thread4

    我们可以看到,即使中途有睡眠过程,也不再出现错误票数,当然这个例子有一些问题,虽然保证了安全,却只能让一个线程完成操作,解决方法很简单,在while循环内设置判断语句,再加锁即可

    对象如同锁,持有锁的线程可以在同步中执行

    没有持有锁的线程即使获取了cpu的执行权,也无法执行同步代码

    如果同步函数被静态修饰后,如何使用锁?

    通过验证,发现不再是this,因为静态方法中也不可以定义this

    静态进入内存时,内存中没有本类对象,但是一定有该类的对应的字节码文件对象 类名.class,该对象的类型是Class

    所以静态的同步方法是,使用该方法所在类的字节码对象作为锁,即类名.class

       1: class Tickets
       2: {
       3:     static int tickets = 10;
       4:     public static void sell()
       5:     {
       6:         //类对象作为锁
       7:         synchronized(Selling.class)
       8:         {
       9:             if(tickets > 0)
      10:             {
      11:                 try{Thread.sleep(500);}catch(Exception e){}
      12:                 System.out.println(Thread.currentThread().getName()+"..tickets = "+(--tickets));
      13:             }
      14:         }
      15:     }
      16: }
      17:  
      18: class Selling implements Runnable
      19: {
      20:     //实现Runnable接口的run方法
      21:     public void run() 
      22:     {
      23:         while(Tickets.tickets> 0)
      24:         {
      25:             Tickets.sell();
      26:         }    
      27:     }
      28: }

    同步的前提:

    1. 必须要有两个或两个以上的线程
    2. 必须要多个线程使用同一个锁

    同步的利弊

    好处:解决了多线程的安全问题

    弊端:多个线程需要判断锁,较为消耗资源

    加入同步之后线程的状态如下

    thread2

    如何编写多线程

    1. 明确哪些代码是多线程运行代码
    2. 明确共享数据
    3. 明确多线程运行代码中哪些语句是操作共享数据的

    死锁

    死锁其实就是同步中嵌套同步

       1:  
       2: class A implements Runnable
       3: {
       4:     String a = "a";
       5:  
       6:     public void run()
       7:     {
       8:         while(true)
       9:         {
      10:             //A抢占A锁
      11:             synchronized(A.class)
      12:             {
      13:                 System.out.println("A get 1");
      14:                 //A抢占B锁
      15:                 synchronized(B.class)
      16:                 {
      17:                     System.out.println("A get 2");
      18:                     System.out.println("A:"+a);
      19:                 }
      20:             }
      21:         }
      22:     }
      23: }
      24:  
      25: class B implements Runnable
      26: {
      27:     String b = "b";
      28:  
      29:     public void run()
      30:     {
      31:         while(true)
      32:         {
      33:             //B抢占B锁
      34:             synchronized(B.class)
      35:             {
      36:                 System.out.println("B get 1");
      37:                 //B抢占A锁
      38:                 synchronized(A.class)
      39:                 {
      40:                     System.out.println("B get 2");
      41:                     System.out.println("B:"+b);
      42:                 }
      43:             }
      44:         }
      45:     }
      46: }
      47: class DeadlockDemo 
      48: {
      49:     public static void main(String[] args) 
      50:     {
      51:         new Thread(new A()).start();
      52:         new Thread(new B()).start();
      53:     }
      54: }

    结果如下,A和B都在等待对方释放资源(对应的锁)

    thread5

  • 相关阅读:
    C# 按笔画排序
    WEB EXCEL OWC开发(老资料)
    JS操作Cookie
    汉字转拼音缩写取首字母
    javaScript通用数据类型校验
    嵌套Repeater 子层获得父层字段值 经典!!!
    不同于其他的provider: SQL 网络接口, error: 26 定位指定的服务器/实例时出错
    C# UDP 发送 接收
    Js 过滤空格
    高亮文本框
  • 原文地址:https://www.cnblogs.com/ShawnWithSmallEyes/p/3378959.html
Copyright © 2011-2022 走看看