线程 Thread
一、定义
进程中的一个执行控制单元,执行路径
线程分类:
- 单线程:安全性高,但是效率低
- 多线程:安全性低,但是效率高
-
二、创建线程的几种方式
- 继承Thread类,重写run()方法
- run()定义线程体
- start() 开启线程
- 实现Runnable接口,重写run()方法 ---推荐
- 实现Callable接口,重写call()方法:了解
2.1、继承Thread类
步骤实例:
//继承Thread类 public class ThreadDemo01 extends Thread{ //定义线程体 @Override public void run() { for(int i=1;i<=100;i++){ System.out.println("一边吃饭....."); } } public static void main(String[] args) { //创建线程对象:创建Thread子类的对象 ThreadDemo01 th=new ThreadDemo01(); //开启线程 :准备好了,cpu可以调度了 th.start(); //th.run(); //方法的调用,单线程,run()执行完毕以后才会继续执行main方法中的代码 //默认主线程 for(int i=1;i<=100;i++){ System.out.println("一边说话....."); } } }
2.2、实现Runnable接口类
步骤实例:
//实现Runnable接口 public class ThreadDemo02 implements Runnable{ //重写run方法 @Override public void run() { for(int i=1;i<=20;i++){ //捕捉异常 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("一边学习....."); } } public static void main(String[] args) { //利用静态代理 ThreadDemo02 th=new ThreadDemo02(); Thread t=new Thread(th); //开启线程 t.start(); for(int i=1;i<=20;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("一边陪女朋友....."); } } }
优点:
- 避免单继承的局限性
- 实现资源共享
-
2.3、匿名内部类实现方式
public class Test01 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"我是匿名内部类"); } try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); } }
2.4、Lambda实现方式
new Thread(()->{ for(int i=0;i<20;i++){ try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("一遍嘿嘿笑..."); } }).start();
2.5、内部类实现方式
public class Demo02 { static class Inner implements Runnable{ //静态内部类 @Override public void run() { for(int i=0;i<20;i++){ try { Thread.sleep(2); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("一遍讲课..."); } } } public static void main(String[] args) { new Thread(new Inner()).start(); } }
2.6、火车购票实例
public class Web12306_03 implements Runnable{ int tickets=100; //100张票 /* * 买票的流程功能 */ @Override public void run() { while(true){ //结束买票的条件 if(tickets<=0){ break; } try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //static Thread currentThread();返回当前线程对象 System.out.println(Thread.currentThread().getName()+"正在买第"+tickets--); } } public static void main(String[] args) { Web12306_03 web=new Web12306_03(); //100 //创建线程 资源共享 Thread th1=new Thread(web,"张三"); Thread th2=new Thread(web,"李四"); Thread th3=new Thread(web,"王五"); //开启线程 th1.start(); th2.start(); th3.start(); } }
注:当多个线程同时操作同一份资源的时候,可能会出现线程不安全问题
三、线程的状态
有5种状态:
- 新生: new Thread()
- 就绪: start(),就绪状态的线程会进入到就绪队列中,等待cpu调度
- 运行: 当cpu非配时间片给线程,这个线程就会进入运行状态开始运行
- 阻塞: sleep()...
- 终止: 线程结束
注:
- 如果一个线程进入阻塞状态,阻塞状态解除也无法直接进入运行状态,会进入就绪状态
- 如果一个线程已经死亡,无法恢复就算重新开启线程也不是原来的那一个
3.1
1)如何进入终止状态
- 正常执行完毕
- 添加外部表示判断
2)如何进入就绪状态
- start()
- 阻塞状态解除
- yield()
- 线程切换
3)如何进入阻塞状态
- sleep()
- wait()
- join()
- IO 操作
sleep(ms) 线程睡眠.. (抱着资源睡觉)
- 模拟网络延迟
- 放大发生问题的可能性
-
3.2、yield()方法
会让出cpu的资源,让线程直接进入就绪状态
public class YieldDemo02 { static class Inner implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"开始执行"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"结束执行"); } } public static void main(String[] args) { Inner in=new Inner(); new Thread(in,"A" ).start(); new Thread(in,"B" ).start(); } }
3.3、join()方法
也叫插队方法,使被插队的线程进入阻塞状态
3.4、获取线程状态的方法
getState() 返回线程的枚举类型状态值
线程的优先级: 1~10 1最小 10最大 默认5
- int getPriority() 返回线程的优先级
- void setPriority(int newPriority) 更改线程的优先级
-
public class StateDemo04 { public static void main(String[] args) { Thread th=new Thread(()->{ for(int i=1;i<=10;i++){ if(i==5){ try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } } } }); System.out.println(th.getState()); th.start(); System.out.println(th.getPriority()); Thread th2=new Thread(()->{System.out.println("哈哈哈2");}); Thread th3=new Thread(()->{System.out.println("哈哈哈3");}); System.out.println(th2.getPriority()); System.out.println(th3.getPriority()); th2.setPriority(1); th3.setPriority(6); th2.start(); th3.start(); } }
四、线程安全
当多线程,同时操作同一份资源的时候有可能存在线程不安全问题
如何控制线程安全:
- 锁-->同步锁 synchronized
-
1)、同步方法
使用关键字synchronized修饰的方法,一旦被一个线程访问,则整个方法全部被锁住,其他线程则无法访问。
public void run() { while(true){ payTicket(); } } public synchronized void payTicket() { if(tickets>0){ try{ Thread.sleep(200); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在买"+tickets--); } }
- 静态方法
-
public static synchronized void payTicket() { if(tickets>0){ try{ Thread.sleep(200); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在 买"+tickets--); } }
2)、同步块
格式: synchronized(引用类型|对象|类)
public void run() { while(true){ //结束买票的条件 //锁类 synchronized (Web12306_02.class) { if(tickets<=0){ break; } try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在买第"+tickets--); } } }
注:
- 锁要锁不变的内容,才能锁住
- 锁的范围太大,效率低,锁的范围太小,锁不住