多线程详解和代码测试

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 }
原文地址:https://www.cnblogs.com/lyywj170403/p/9305632.html