多线程详解和代码测试
Posted lyywj170403
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程详解和代码测试相关的知识,希望对你有一定的参考价值。
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 }
以上是关于多线程详解和代码测试的主要内容,如果未能解决你的问题,请参考以下文章