大数据之路week03--day05(线程 I)
真的,身体这个东西一定要爱护好,难受的时候电脑都不想去碰,尤其是胃和肾。。。
这两天耽误了太多时间,今天好转了立刻学习,即刻不能耽误!、
话不多说,说正事:
1、多线程(理解)
(1)多线程:一个应用程序有多条执行路径
进程:正在执行的应用程序
线程:进程的执行单元,或者说是执行路径
单线程:一个应用程序只有一条执行路径
多线程:一个应用程序有多条执行路径
多进程的意义何在?
提高CPU的使用率
多线程的意义何在?
提高应用程序的使用率
问题:
一边玩游戏,一边听歌时同时进行的吗?
不是,因为单CPU在某一个时间点上只能做一件事。
而我们在玩游戏,或者听歌的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。
(2)Java程序的运行原理及JVM的启动是多线程的吗?
A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程。
B: JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。
两个词汇:并行和并发
并行是逻辑上同时发生,指在某一个时间段内同时运行多个程序。
并发是物理上同时发生,指在某一个时间点同时运行多个程序。
高并发:在某个时间点上很多人去访问一个(大数据)
(3)多线程的实现方案
A: 继承Thread类
步骤:
1、自定义类MyThread继承Thread类
2、MyThread类里面重写run()
3、创建对象
4、启动线程
MyThread类
1 package com.wyh.Thread01; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午3:04:30 6 */ 7 public class MyThread extends Thread { 8 @Override 9 public void run() { 10 for(int x = 0; x<500;x++) { 11 System.out.println(x); 12 } 13 } 14 15 }
测试类:
1 package com.wyh.Thread01; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午3:05:18 6 */ 7 public class ThreadDemo01 { 8 public static void main(String[] args) { 9 MyThread my1 = new MyThread(); 10 MyThread my2 = new MyThread(); 11 12 my1.start(); 13 my2.start(); 14 } 15 16 17 }
B: 实现Runnable接口
1、自定义类MyThread实现Runnable接口
2、MyThread类里面重写run()
3、创建对象
4、创建Thread对象,把MyThread对象当作参数传入
5、启动线程
Runnable类
1 package com.wyh.Thread04; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午7:00:06 6 */ 7 public class MyRunnable implements Runnable { 8 9 @Override 10 public void run() { 11 for (int x = 1; x <= 500; x++) { 12 System.out.println(Thread.currentThread().getName() + "--" + x); 13 } 14 15 } 16 17 }
测试类:
1 package com.wyh.Thread04; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午7:01:07 6 */ 7 public class MyRunnableDemo { 8 public static void main(String[] args) { 9 MyRunnable my = new MyRunnable(); 10 Thread t1 = new Thread(my,"王友虎"); 11 Thread t2 = new Thread(my,"赵以浩"); 12 13 t1.start(); 14 t2.start(); 15 16 } 17 18 }
(4)线程的调度和优先级问题
A:线程的调度
a:分时调度
b:抢占式调度(Java采用的是该调度方式)
B:获取和设置线程的优先级
a:默认是5
b:范围是1-10
(5)线程的控制(常见方法)
A:休眠线程(测试类在这不写,基本不变)
1 package com.wyh.Thread03; 2 3 import java.util.Date; 4 5 /** 6 * @author WYH 7 * @version 2019年11月22日 下午3:04:30 8 */ 9 public class ThreadSleep extends Thread { 10 public ThreadSleep() { 11 super(); 12 // TODO Auto-generated constructor stub 13 } 14 15 public ThreadSleep(String name) { 16 super(name); 17 // TODO Auto-generated constructor stub 18 } 19 20 @Override 21 public void run() { 22 for(int x = 0; x<500;x++) { 23 System.out.println(getName()+x+" 日期:"+new Date()); 24 //睡眠1秒 25 try { 26 Thread.sleep(1000); 27 } catch (InterruptedException e) { 28 // TODO Auto-generated catch block 29 e.printStackTrace(); 30 } 31 32 } 33 } 34 35 }
B:加入线程(run方法类不变)
1 package com.wyh.Thread03; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午3:05:18 6 * 7 * join 为了让某些线程执行完毕,才能执行其他的(线程加入) 8 * 9 * 10 * 11 */ 12 public class ThreadJoinDemo { 13 public static void main(String[] args) { 14 15 ThreadPriority my1 = new ThreadPriority("王友虎"); 16 ThreadPriority my2 = new ThreadPriority("李宏灿"); 17 ThreadPriority my3 = new ThreadPriority("齐博源"); 18 19 my1.start(); 20 try { 21 my1.join(); 22 } catch (InterruptedException e) { 23 // TODO Auto-generated catch block 24 e.printStackTrace(); 25 } 26 27 my2.start(); 28 my3.start(); 29 30 } 31 32 33 }
C:礼让线程(省略测试类代码)
1 package com.wyh.Thread03; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午4:28:15 6 */ 7 public class ThreadYield extends Thread { 8 public ThreadYield() { 9 super(); 10 // TODO Auto-generated constructor stub 11 } 12 13 public ThreadYield(String name) { 14 super(name); 15 // TODO Auto-generated constructor stub 16 } 17 18 @Override 19 public void run() { 20 for(int x = 0; x<200;x++) { 21 System.out.println(getName()+x); 22 Thread.yield(); 23 } 24 } 25 }
D:后台线程(省略继承类的代码)//注意!!!!必须在启动前声明
1 package com.wyh.Thread03; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午4:42:16 6 * 7 * 守护线程 8 * 9 * 理解:坦克大战,黄绿坦克守护家,家没了,坦克也没了 10 * 11 */ 12 public class ThreadDeamonDemo { 13 public static void main(String[] args) { 14 ThreadDeamon td1 = new ThreadDeamon("关羽"); 15 ThreadDeamon td2 = new ThreadDeamon("张飞"); 16 17 18 //注意!!!!必须在启动前声明 19 td1.setDaemon(true); 20 td2.setDaemon(true); 21 22 td1.start(); 23 td2.start(); 24 25 26 27 28 Thread.currentThread().setName("刘备"); 29 for (int x = 0; x < 5; x++) { 30 System.out.println(Thread.currentThread().getName() + x); 31 } 32 } 33 34 }
E:终止线程(不推荐使用stop,而且该方法已经过时,推荐使用interrupt)
继承类:
1 package com.wyh.Thread03; 2 3 import java.util.Date; 4 5 /** 6 * @author WYH 7 * @version 2019年11月22日 下午4:57:38 8 */ 9 public class ThreadStop extends Thread{ 10 @Override 11 public void run() { 12 System.out.println("时间:"+new Date()); 13 14 //睡眠10秒钟 15 try { 16 Thread.sleep(10000); 17 } catch (InterruptedException e) { 18 System.err.println("睡眠被意外中止!!"); 19 } 20 21 System.out.println("时间:"+new Date()); 22 23 } 24 }
测试类:
1 package com.wyh.Thread03; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午5:00:21 6 */ 7 public class ThreadStopDemo { 8 public static void main(String[] args) { 9 ThreadStop ts = new ThreadStop(); 10 ts.start(); 11 12 try { 13 Thread.sleep(3000); 14 // ts.stop(); //不建议使用 因为后面的代码无法执行 15 ts.interrupt(); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 20 21 } 22 23 }
(6)线程的生命周期(图解)
(7)电影院卖票程序的实现
A:继承Thread类
继承类:
1 package com.wyh.Thread05; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午7:33:38 6 */ 7 public class SellTicket extends Thread { 8 private static int tickets = 100; 9 10 @Override 11 public void run() { 12 while(true) { 13 if(tickets>0) { 14 System.out.println(getName()+"窗口正在售卖第"+(tickets--)+"张票。。"); 15 } 16 17 } 18 } 19 }
测试类:
1 package com.wyh.Thread05; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午7:36:19 6 */ 7 public class TicketDemo { 8 public static void main(String[] args) { 9 //先创建多个线程 10 SellTicket st1 = new SellTicket(); 11 SellTicket st2 = new SellTicket(); 12 SellTicket st3 = new SellTicket(); 13 14 //给多个线程起名字’ 15 st1.setName("窗口1"); 16 st2.setName("窗口2"); 17 st3.setName("窗口3"); 18 19 st1.start(); 20 st2.start(); 21 st3.start(); 22 23 24 25 } 26 27 }
B:实现Runnable接口
实现Runnable接口类:
1 package com.wyh.Thread05; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午7:42:37 6 */ 7 public class SellTicket_Runnable implements Runnable { 8 private int tickets = 100; 9 10 @Override 11 public void run() { 12 while(true) { 13 if(tickets>0) { 14 System.out.println(Thread.currentThread().getName()+"窗口正在售卖第"+(tickets--)+"张票。。"); 15 } 16 } 17 18 } 19 20 }
测试类:
1 package com.wyh.Thread05; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午7:43:48 6 */ 7 public class SellTicket_RunnableDemo { 8 public static void main(String[] args) { 9 SellTicket_Runnable sr = new SellTicket_Runnable(); 10 11 Thread t1 = new Thread(sr,"窗口1"); 12 Thread t2 = new Thread(sr,"窗口2"); 13 Thread t3 = new Thread(sr,"窗口3"); 14 15 t1.start(); 16 t2.start(); 17 t3.start(); 18 19 } 20 21 }
(8)电影院卖票程序出的问题
A:为了更加符合真实场景,加入了休眠100毫秒。
B:买票问题
a:同票被多次出售
b:出现0票和负数票
(9)多线程安全问题的原因(也是我们以后判断一个程序是否有安全问题的依据)
A:是否有多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据
(10)同步解决线程安全问题(以解决上面售票的为例解决)
A:同步代码块
synchronized(对象){
需要被同步的代码;
}
这里的对象可以是任意对象(充当锁的作用,下面也是相同作用)
实现接口类:(测试类代码不变)
1 package com.wyh.Thread06_tickets; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午7:42:37 6 */ 7 public class SellTicket_Runnable implements Runnable { 8 private int tickets = 300; 9 private Object obj = new Object(); //如同一把锁 多个线程共用一把锁,把对共享数据的操作包起来 10 11 @Override 12 public void run() { 13 while(true) { 14 synchronized(obj) { 15 if(tickets>0) { 16 try { 17 Thread.sleep(30); 18 } catch (InterruptedException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } 22 System.out.println(Thread.currentThread().getName()+"窗口正在售卖第"+(tickets--)+"张票。。"); 23 } 24 } 25 26 } 27 28 } 29 30 }
B:同步方法
把同步放在方法上。
这里的锁对象是 this
实现接口类:(测试类代码不变)
1 package com.wyh.Thread07_tickets2_同步方法静态; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午7:42:37 6 */ 7 public class SellTicket_Runnable implements Runnable { 8 private int tickets = 300; 9 private int x = 0; 10 11 //用obj对象做锁 12 private Object obj = new Object(); //如同一把锁 多个线程共用一把锁,把对共享数据的操作包起来 13 14 15 @Override 16 public void run() { 17 if(x%2==0) { 18 while(true) { 19 synchronized(this) { 20 if(tickets>0) { 21 try { 22 Thread.sleep(30); 23 } catch (InterruptedException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 System.out.println(Thread.currentThread().getName()+"窗口正在售卖第"+(tickets--)+"张票。。"); 28 } 29 } 30 31 } 32 }else { 33 synchronize(); 34 } 35 36 37 } 38 39 40 private synchronized void synchronize() { 41 while(true) { 42 if(tickets>0) { 43 try { 44 Thread.sleep(30); 45 } catch (InterruptedException e) { 46 // TODO Auto-generated catch block 47 e.printStackTrace(); 48 } 49 System.out.println(Thread.currentThread().getName()+"窗口正在售卖第"+(tickets--)+"张票。。"); 50 } 51 52 } 53 54 } 55 }
C:静态同步方法
把同步加在方法上。
这里的锁对象是当前类的字节码文件对象(反射)
1 package com.wyh.Thread07_tickets2_同步方法静态; 2 3 /** 4 * @author WYH 5 * @version 2019年11月22日 下午7:42:37 6 */ 7 public class SellTicket_Runnable2 implements Runnable { 8 private static int tickets = 300; 9 private int x = 0; 10 11 12 13 @Override 14 public void run() { 15 if(x%2==0) { 16 while(true) { 17 synchronized(SellTicket_Runnable2.class) { 18 if(tickets>0) { 19 try { 20 Thread.sleep(30); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 System.out.println(Thread.currentThread().getName()+"窗口正在售卖第"+(tickets--)+"张票。。"); 25 } 26 } 27 28 } 29 }else { 30 synchronize(); 31 } 32 33 34 } 35 36 37 private synchronized static void synchronize() { 38 while(true) { 39 if(tickets>0) { 40 try { 41 Thread.sleep(30); 42 } catch (InterruptedException e) { 43 e.printStackTrace(); 44 } 45 System.out.println(Thread.currentThread().getName()+"窗口正在售卖第"+(tickets--)+"张票。。"); 46 } 47 48 } 49 50 } 51 }
· (11)回顾以前的线程安全的类
A:StringBuffer
B:Vector
C:HashTable
D:如何把一个线程不安全的集合变成一个线程安全的集合类
用Collections 工具类的方法即可。
以List集合为例:其他集合创建方法类似,具体看API
1 package Collections保证线程安全的; 2 3 import java.util.ArrayList; 4 import java.util.Collections; 5 import java.util.List; 6 7 /** 8 * @author WYH 9 * @version 2019年11月22日 下午9:10:58 10 */ 11 public class Demo { 12 public static void main(String[] args) { 13 //以List集合为例 14 //以前的我们是这么做的 15 List<String> list1 = new ArrayList<String>(); //这么创建的List是不安全的 16 17 18 19 //Collections 工具类提供的方法 同步安全的 20 List<String> list2 = Collections.synchronizedList(new ArrayList<String>()); 21 22 list2.add("1"); 23 list2.add("2"); 24 25 for(String s : list2) { 26 System.out.println(s); 27 } 28 } 29 30 }
线程中需要注意的问题:
1、Thread中start()方法的功能就是创建一个新的线程,并自动调用该线程的run()方法,直接调用run()方法是不会创建一个新的线程的,直接调用相当于调用一个普通方法。
2、执行一个线程实际就是执行该线程run方法中的代码
3、一个Thread对象只能代表一个线程。
一个Thread对象不能调用两次start()方法,否则会抛出java.lang.IllegalThreadStateException异常