zoukankan      html  css  js  c++  java
  • 多线程详解和代码测试

    1:多线程
    (1)多线程:一个应用程序有多条执行路径
    进程:正在执行的应用程序。

                 是系统进行资源分配和调用的独立单元。每一个进程都有他自己的内存空间和系统资源
    线程:进程的执行单元,执行路径。

                  在同一个进程内又可以执行多个任务,而这每一个任务就可以视为一个线程。
    单线程:一个应用程序只有一条执行路径
    多线程:一个应用程序有多条执行路径

    多进程的意义?
    提高CPU的使用率


    多线程的意义?
    提高应用程序的使用率


    (2)Java程序的运行原理及JVM的启动是多线程的吗?
    A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程。
    B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。

    (3)多线程的实现方案
    A:继承Thread类

     1 /*
     2  * java提供两两种方式实现多线程程序
     3  * 
     4  * 方式一:继承Thred类
     5  * 步骤:自定义MyThread类 继承Thred类 
     6  *     MyThread类里面重写run()?
     7  *        这里要明白为什么是run方法
     8  *     创建对象
     9  *     启动线程
    10  *     
    11  */
    12 public class MyThreadDemo {
    13     public static void main(String[] args) {
    14         //创建线程对象
    15         //MyThread my = new MyThread();
    16         //启动线程
    17         //my.run();
    18         //my.run();
    19         /*
    20          * 调用run()方法为什么是单线程的?
    21          * 由于run()方法直接调用其实就相当于普通的方法调用,所有是单线程的效果
    22          * 可以通过start()方法来实现多线程
    23          * 
    24          * 这里要明白run()和start()的区别:
    25          * run():仅是封装被线程执行的代码,直接调用属于普通方法
    26          * start():先启动线程,然后再通过jvm去调用该线程的run()方法
    27          */
    28         
    29         //创建两个线程
    30         MyThread my1 = new MyThread();
    31         MyThread my2 = new MyThread();
    32         my1.start();
    33         my2.start();
    34     }
    35 
    36 }
     1 /*
     2  * 这个类要重写run()方法,是因为,不是说类中的所有代码都需要被线程执行。
     3  * 这时为了区分哪些代码能够被线程执行,Java就提供了Thread类中的run()方法
     4  * 来包含哪些需要被线程执行的代码
     5  */
     6 public class MyThread extends Thread{
     7     @Override
     8     public void run(){
     9         for(int x = 0; x < 100; x++){
    10             System.out.println(x);
    11         }
    12     }
    13 
    14 }

    获取线程名称

     1 /*
     2  * 获取线程对象名称
     3  */
     4 public class MyThredDemo1 {
     5     public static void main(String[] args) {
     6         //带参构造方法给线程起名字
     7         MyThread1 my1 = new MyThread1("a");
     8         MyThread1 my2 = new MyThread1("b");
     9         
    10         //调用方法设置名称
    11         //my1.setName("a");
    12         //my2.setName("b");
    13         
    14         my1.start();
    15         my2.start();
    16         
    17         //获取main方法所在线程名称
    18         System.out.println(Thread.currentThread().getName());
    19     }
    20 
    21 }
     1 public class MyThread1 extends Thread{
     2 
     3     public  MyThread1() {
     4         
     5     }
     6     
     7     public  MyThread1(String name) {
     8         
     9     }
    10     
    11     @Override
    12     public void run(){
    13         for(int x = 0; x < 100; x++){
    14             System.out.println(getName() + ":" + x);
    15         }
    16     }
    17 }


    B:实现Runnable接口(一般都会采用方式二)

     1 /*
     2  * 多线程实现方式2:实现Runnable接口
     3  * 步骤:1.自定义类MyRunnable实现Runnable接口
     4  *     2.重写run()方法
     5  *     3.创建MyRunnable类的对象
     6  *     4.创建Thread类的对象,并把3步骤的对象作为构造参数传递
     7  */
     8 public class MyRunnableDemo {
     9      public static void main(String[] args) {
    10          //创建MyRunnable类的对象
    11         MyRunnable my = new MyRunnable();
    12         
    13         //创建Thread类的对象,并把3步骤的对象作为构造参数传递
    14         //Thread(Runnale target)
    15         //Thread t1 = new Thread(my);
    16         //Thread t2 = new Thread(my);
    17         //t1.setName("aa");
    18         //t2.setName("bb");
    19         
    20         //Thread(Runnable target, String name) 另一种方法命名
    21         Thread t1 = new Thread(my, "aa");
    22         Thread t2 = new Thread(my, "bb");
    23         
    24         t1.start();
    25         t2.start();
    26    }
    27 }
     1 public class MyRunnable implements Runnable {
     2 /*
     3  * 1.自定义类MyRunnable实现Runnable接口
     4  * 2.重写run()方法
     5  * 
     6  */
     7     @Override
     8     public void run() {
     9         for(int x = 0; x < 100; x++){
    10             //由于实现接口的方法不能直接Thread类的方法,这时需要间接使用
    11             System.out.println(Thread.currentThread().getName() + ":" + x);
    12         }
    13 
    14     }
    15 
    16 }

    小结:

    实现多线程的方式:两种

    方式一:继承Thread类

    步骤:1.自定义类MyThread继承Thread类

               2.在MyThread类中重写run()方法

               3.创建MyThread类的对象

               4.启动线程对象

    方式二:实现Runnable接口

    步骤:1.自定义类MyRunnble实现Runnable接口

               2.MyRunnble里面重写run()方法

               3.创建MyRunnble类的对象

               4.创建Thread类的对象,并把3步骤的对象作为构造参数传递

    问题:有了方式一了,为什么还要方式二呢?

              1.可以避免由于Java单继承带来的局限性

              2.适合多个相同程序的代码去处理同一个资源的情况,将线程和程序的代码、数据分离,充分体现了面向对象的设计思想。


    (4)线程的调度和优先级问题
        A:线程的调度
          a:分时调度
          b:抢占式调度 (Java采用的是该调度方式)
       B:获取和设置线程优先级
          a:默认是5
          b:范围是1-10

     1 /*
     2  * 获取线程的优先级
     3  *    public final int getPriority():返回线程的优先级
     4  *    
     5  * 设置线程对象的优先级
     6  *    public final void setPriority():设置线程优先级
     7  *    
     8  * 注意:线程默认的优先级是5
     9  *     线程优先级范围是:1-10
    10  *     优先级高仅表示线程获取CPU时间片的几率高,这要求次数比较多或者多次运行才能看到好的效果
    11  *     
    12  */
    13 public class ThreadPriorityDemo {
    14 
    15     public static void main(String[] args) {
    16         ThreadPriority tp1 = new ThreadPriority();
    17         ThreadPriority tp2 = new ThreadPriority();
    18         ThreadPriority tp3 = new ThreadPriority();
    19         
    20         tp1.setName("aa");
    21         tp1.setName("bb");
    22         tp1.setName("cc");
    23         
    24         //获取默认优先级
    25         System.out.println(tp1.getPriority());
    26         System.out.println(tp2.getPriority());
    27         System.out.println(tp3.getPriority());
    28         
    29         //设置优先级
    30         tp1.setPriority(10);
    31         tp2.setPriority(1);
    32         
    33         tp1.start();
    34         tp2.start();
    35         tp3.start();
    36     }
    37 }
     1 public class ThreadPriority extends Thread{
     2     @Override
     3     public void run() {
     4         for(int x = 0; x < 100; x++){
     5             System.out.println(getName() + ":" + x);
     6         }
     7         
     8     }
     9 
    10 }


    (5)线程的控制(常见方法)
    A:休眠线程

     1 //线程睡眠  public static void sleep(long millis)
     2 public class ThreadSleepDemo {
     3  
     4     public static void main(String[] args) {
     5         ThreadSleep ts1 = new ThreadSleep();
     6         ThreadSleep ts2 = new ThreadSleep();
     7         ThreadSleep ts3 = new ThreadSleep();
     8         
     9         //设置线程名称
    10         ts1.setName("a");
    11         ts2.setName("b");
    12         ts3.setName("c");
    13         
    14         //启动线程
    15         ts1.start();
    16         ts2.start();
    17         ts3.start();
    18         
    19     }
    20     
    21 }
     1 public class ThreadSleep extends Thread {
     2 
     3     @Override
     4     public void run() {
     5         for(int x = 0; x < 100; x++){
     6             System.out.println(getName() + ":" + x + ",时间:" + new Date());
     7             //睡眠
     8             try {
     9                 Thread.sleep(1000);//睡眠
    10             } catch (InterruptedException e) {
    11                 e.printStackTrace();
    12             }
    13             
    14         }
    15         
    16     }
    17 }


    B:加入线程

     1 //public final void join():等待线程终止
     2 public class ThreadJoinDemo {
     3 
     4     public static void main(String[] args) {
     5         ThreadJoin tj1 = new ThreadJoin();
     6         ThreadJoin tj2 = new ThreadJoin();
     7         ThreadJoin tj3 = new ThreadJoin();
     8         
     9         tj1.setName("aa");
    10         tj2.setName("bb");
    11         tj3.setName("cc");
    12         
    13         tj1.start(); 
    14         //等待tj1执行完
    15         try {
    16             tj1.join();
    17         } catch (InterruptedException e) {
    18             e.printStackTrace();
    19         }
    20         
    21         tj2.start();
    22         tj3.start();
    23         
    24     }
    25 }
    1 public class ThreadJoin extends Thread {
    2     @Override
    3     public void run() {
    4         for(int x = 0; x < 100; x++){
    5             System.out.println(getName() + ":" + x);
    6 
    7         }
    8     }
    9 }


    C:礼让线程

     1 /*
     2  * public static void yield():暂停当前正在执行的线程,并去执行其他线程
     3  * 可以让多个线程更加和谐,但是还不能保证完全均匀
     4  */
     5 public class ThreadYieldDemo {
     6     public static void main(String[] args) {
     7         ThreadYield ty1 = new ThreadYield();
     8         ThreadYield ty2 = new ThreadYield();
     9         
    10         ty1.setName("a");
    11         ty2.setName("b");
    12         
    13         ty1.start();
    14         ty2.start();
    15     }
    16 
    17 }
     1 public class ThreadYield extends Thread{
     2     @Override
     3     public void run() {
     4         for(int x = 0; x < 100; x++){
     5             System.out.println(getName() + ":" + x);
     6 
     7             Thread.yield();
     8         }
     9     }
    10 
    11 }


    D:后台线程(守护线程)

     1 /*
     2  * public final void setDaemon(boolean on)
     3  * 将该线程标记为守护线程或者用户线程
     4  * 当正在运行的线程都是守护线程时,java虚拟机退出
     5  * 该方法必须在启动前调用
     6  */
     7 public class ThreadDaemonDemo {
     8 
     9     public static void main(String[] args) {
    10         ThreadDaemon td1 = new ThreadDaemon();
    11         ThreadDaemon td2 = new ThreadDaemon();
    12         
    13         td1.setName("aa");
    14         td2.setName("bb");
    15         
    16         //设置守护线程
    17         td1.setDaemon(true);
    18         td2.setDaemon(true);
    19         
    20         td1.start();
    21         td2.start();
    22         
    23         //设置主类线程名称
    24         Thread.currentThread().setName("dd");
    25         for(int x = 0; x < 5; x++){
    26             System.out.println(Thread.currentThread().getName() + ":" + x);
    27         }
    28     }
    29 }
    1 public class ThreadDaemon extends Thread{
    2     @Override
    3     public void run() {
    4         for(int x = 0; x < 100; x++){
    5             System.out.println(getName() + ":" + x);
    6         }
    7     }
    8 
    9 }


    E:终止线程(掌握)

     1 public class ThreadStopDemo {
     2 
     3     public static void main(String[] args) {
     4         ThreadStop ts = new ThreadStop();
     5         
     6         ts.start();
     7         
     8         try {
     9             Thread.sleep(3000);
    10             ts.interrupt();
    11         } catch (InterruptedException e) {
    12             
    13             e.printStackTrace();
    14         }
    15     }
    16 }
     1 public class ThreadStop extends Thread{
     2     @Override
     3     public void run() {
     4         System.out.println("开始时间:" + new Date());
     5         try {
     6             Thread.sleep(10000);
     7         } catch (InterruptedException e) {
     8             
     9             //e.printStackTrace();
    10             System.out.println("线程被中断");
    11         }
    12         
    13         System.out.println("结束时间:" + new Date());
    14     }
    15 
    16 }


    (6)线程的生命周期
    A:新建
    B:就绪
    C:运行
    D:阻塞
    E:死亡


    (7)电影院卖票程序的实现
    A:继承Thread类

     1 //继承Thread类来实现
     2 public class SellTicketDemo {
     3 
     4     public static void main(String[] args) {
     5         //三个窗口  创建三个线程对象
     6         SellTicket st1 = new SellTicket();
     7         SellTicket st2 = new SellTicket();
     8         SellTicket st3 = new SellTicket();
     9         
    10         //设置线程对象名称
    11         st1.setName("窗口1");
    12         st2.setName("窗口2");
    13         st3.setName("窗口3");
    14         
    15         //启动线程
    16         st1.start();
    17         st2.start();
    18         st3.start();
    19     }
    20 }
     1 public class SellTicket extends Thread {
     2 
     3     //定义100张票
     4     //private int tickets = 100;
     5     //为了让多个线程对象共享这100张票,将其用静态修饰
     6     private static int tickets = 100;
     7     @Override
     8     public void run() {
     9         //定义100张票
    10         //这里每个线程进来都会走这里,相当于每个线程卖自己的100张票,将票数定义到外面
    11         //int tickets = 100;
    12         
    13         //表示电影院一直有票
    14         while(true){
    15             if(tickets > 0){
    16                 System.out.println(getName() + "正在出售第:" + (tickets--) + "张票");
    17             }
    18         }
    19     }
    20 }


    B:实现Runnable接口(多线程问题一般采用这种方法)

     1 //实现Runnable接口的方法
     2 public class SellTicketDemo {
     3 
     4     public static void main(String[] args) {
     5         //创建资源对象
     6         SellTicket st = new SellTicket();
     7         
     8         //创建三个窗口(线程对象)
     9         Thread t1 = new Thread(st, "窗口1");
    10         Thread t2 = new Thread(st, "窗口2");
    11         Thread t3 = new Thread(st, "窗口3");
    12         
    13         //启动线程
    14         t1.start();
    15         t2.start();
    16         t3.start();
    17     }
    18 }
     1 public class SellTicket implements Runnable {
     2     //定义100张票
     3     private int tickets = 100;
     4     
     5     @Override
     6     public void run() {
     7         while(true){
     8             if(tickets > 0){
     9                 System.out.println(Thread.currentThread()
    10                         .getName()+ "正在出售第:" + (tickets--) + "张票" );
    11             }
    12         }
    13     }
    14 
    15 }


    (8)电影院卖票程序出问题
    A:为了更符合真实的场景,加入了休眠100毫秒。
    B:加入休眠后卖票问题
    a:同票多次卖

       因为CPU的一次操作必须是原子性的
    b:负数票

       随机性和延迟性导致


    (9)多线程安全问题的原因

    (也是以后判断一个程序是否有线程安全问题的依据)
    A:是否有多线程环境
    B:是否有共享数据
    C:是否有多条语句操作共享数据

    (10)同步解决线程安全问题
    A:同步代码块
    synchronized(对象) {
        需要被同步的代码;
    }


        注意:同步可以解决安全问题的根本原因在于这个对象上面

                   该对象如同锁的功能。多个线程必须是同一把锁。

     1 //实现Runnable接口的方法
     2 public class SellTicketDemo {
     3 
     4     public static void main(String[] args) {
     5         //创建资源对象
     6         SellTicket st = new SellTicket();
     7         
     8         //创建三个窗口(线程对象)
     9         Thread t1 = new Thread(st, "窗口1");
    10         Thread t2 = new Thread(st, "窗口2");
    11         Thread t3 = new Thread(st, "窗口3");
    12         
    13         //启动线程
    14         t1.start();
    15         t2.start();
    16         t3.start();
    17     }
    18 }
     1 public class SellTicket implements Runnable {
     2     //定义100张票
     3     private int tickets = 100;
     4     
     5     //创建锁对象 同一把锁
     6     private Object obj = new Object();
     7     
     8     @Override
     9     public void run() {
    10         while(true){
    11             synchronized (obj) {
    12             if(tickets > 0){
    13                 try {
    14                     Thread.sleep(100);
    15                 } catch (InterruptedException e) {
    16                     e.printStackTrace();
    17                 }
    18                 System.out.println(Thread.currentThread()
    19                         .getName()+ "正在出售第:" + (tickets--) + "张票" );
    20              }
    21          }
    22      }
    23    }
    24 }

         这里的锁对象可以是任意对象。

     1 //实现Runnable接口的方法
     2 public class SellTicketDemo {
     3 
     4     public static void main(String[] args) {
     5         //创建资源对象
     6         SellTicket st = new SellTicket();
     7         
     8         //创建三个窗口(线程对象)
     9         Thread t1 = new Thread(st, "窗口1");
    10         Thread t2 = new Thread(st, "窗口2");
    11         Thread t3 = new Thread(st, "窗口3");
    12         
    13         //启动线程
    14         t1.start();
    15         t2.start();
    16         t3.start();
    17     }
    18 }
     1 public class SellTicket implements Runnable {
     2     //定义100张票
     3     private int tickets = 100;
     4     
     5     //创建锁对象 同一把锁
     6     //private Object obj = new Object();
     7     //任意对象
     8     private Demo d = new Demo();
     9     @Override
    10     public void run() {
    11         while(true){
    12             synchronized (d) {
    13             if(tickets > 0){
    14                 try {
    15                     Thread.sleep(100);
    16                 } catch (InterruptedException e) {
    17                     e.printStackTrace();
    18                 }
    19                 System.out.println(Thread.currentThread()
    20                         .getName()+ "正在出售第:" + (tickets--) + "张票" );
    21              }
    22          }
    23      }
    24    }
    25 }
    26 
    27 class Demo{
    28     
    29 }

    B:同步方法
    把同步加在方法上。

    这里的锁对象是this

     1 //实现Runnable接口的方法
     2 public class SellTicketDemo {
     3 
     4     public static void main(String[] args) {
     5         //创建资源对象
     6         SellTicket st = new SellTicket();
     7         
     8         //创建三个窗口(线程对象)
     9         Thread t1 = new Thread(st, "窗口1");
    10         Thread t2 = new Thread(st, "窗口2");
    11         Thread t3 = new Thread(st, "窗口3");
    12         
    13         //启动线程
    14         t1.start();
    15         t2.start();
    16         t3.start();
    17     }
    18 }
     1 public class SellTicket implements Runnable {
     2     //定义100张票
     3     private int tickets = 100;
     4     
     5     //创建锁对象 同一把锁
     6     //private Object obj = new Object();
     7     
     8     //任意对象
     9     private Demo d = new Demo();
    10     
    11     private int x = 0;
    12     @Override
    13     public void run() {
    14         while(true){
    15             if(x%2 == 0){
    16                 synchronized (this) {
    17                     if(tickets > 0){
    18                         try {
    19                             Thread.sleep(100);
    20                         } catch (InterruptedException e) {
    21                             e.printStackTrace();
    22                         }
    23                         System.out.println(Thread.currentThread()
    24                                 .getName()+ "正在出售第:" + (tickets--) + "张票" );
    25                      }
    26                  }
    27              
    28             }else{
    29                 sellTicket();
    30             }
    31             x++;
    32             
    33      }
    34   }
    35 
    36   private synchronized void sellTicket() {
    37     
    38         if(tickets > 0){
    39             try {
    40                 Thread.sleep(100);
    41             } catch (InterruptedException e) {
    42                 e.printStackTrace();
    43             }
    44             System.out.println(Thread.currentThread()
    45                     .getName()+ "正在出售第:" + (tickets--) + "张票" );
    46          }
    47      }
    48         
    49 }
    50 
    51 class Demo{
    52  }


    C:静态同步方法
    把同步加在方法上。

    这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)

     1 //实现Runnable接口的方法
     2 public class SellTicketDemo {
     3 
     4     public static void main(String[] args) {
     5         //创建资源对象
     6         SellTicket st = new SellTicket();
     7         
     8         //创建三个窗口(线程对象)
     9         Thread t1 = new Thread(st, "窗口1");
    10         Thread t2 = new Thread(st, "窗口2");
    11         Thread t3 = new Thread(st, "窗口3");
    12         
    13         //启动线程
    14         t1.start();
    15         t2.start();
    16         t3.start();
    17     }
    18 }
     1 public class SellTicket implements Runnable {
     2     //定义100张票
     3     private static int tickets = 100;
     4     
     5     //创建锁对象 同一把锁
     6     //private Object obj = new Object();
     7     
     8     //任意对象
     9     private Demo d = new Demo();
    10     
    11     private int x = 0;
    12     @Override
    13     public void run() {
    14         while(true){
    15             if(x%2 == 0){
    16                 synchronized (SellTicket.class) {
    17                     if(tickets > 0){
    18                         try {
    19                             Thread.sleep(100);
    20                         } catch (InterruptedException e) {
    21                             e.printStackTrace();
    22                         }
    23                         System.out.println(Thread.currentThread()
    24                                 .getName()+ "正在出售第:" + (tickets--) + "张票" );
    25                      }
    26                  }
    27              
    28             }else{
    29                 sellTicket();
    30             }
    31             x++;
    32             
    33      }
    34   }
    35 
    36   private static synchronized void sellTicket() {
    37     
    38         if(tickets > 0){
    39             try {
    40                 Thread.sleep(100);
    41             } catch (InterruptedException e) {
    42                 e.printStackTrace();
    43             }
    44             System.out.println(Thread.currentThread()
    45                     .getName()+ "正在出售第:" + (tickets--) + "张票" );
    46          }
    47      }
    48         
    49 }
    50 
    51 class Demo{
    52  }

    (11)回顾以前的线程安全的类
    A:StringBuffer
    B:Vector
    C:Hashtable
    D:如何把一个线程不安全的集合类变成一个线程安全的集合类
    用Collections工具类的方法即可。

     1 public class ThreadDemo3 {
     2 public static void main(String[] args) {
     3     //线程安全的类
     4     StringBuffer sb = new StringBuffer();
     5     Vector<String> v = new Vector<String>();
     6     Hashtable<String, String> hsah = new Hashtable<String, String>();
     7     
     8     //Vector是线程安全的时候考虑使用。但即使安全也不会考虑使用
     9     //用下面这种安全的
    10     List<String> list1 = new ArrayList<String>();//线程不安全
    11     //线程安全
    12     List<String> list2 = Collections.synchronizedList(new ArrayList<String>());
    13  }
    14 }
  • 相关阅读:
    三剑客
    走近SQL Server的游标
    PostSharp的AOP设计在.NET Remoting中的应用
    总结在使用VB 6.0和C#编写ActiveX控件的实践 (一)
    动态为程序指定快捷键
    为Reporting Service部署自定义程序集可能遇到的问题
    如何更改服务器名称
    如何产生固定的随机数(VBA)
    使用HTTP发送消息(消息队列技术)
    使用TransactionScope做分布式事务协调
  • 原文地址:https://www.cnblogs.com/lyywj170403/p/9305632.html
Copyright © 2011-2022 走看看