多线程
一:java语言中,实现线程的两种方式
java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。
第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。 // 定义线程类 public class MyThread extends Thread{ public void run(){ } } // 创建线程对象 MyThread t = new MyThread(); // 启动线程。 t.start(); 第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。 // 定义一个可运行的类 public class MyRunnable implements Runnable { public void run(){ } } // 创建线程对象 Thread t = new Thread(new MyRunnable()); // 启动线程 t.start(); 注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承 其它的类,更灵活。 第三种方式: 实现Callable接口,重写call()方法,-->juc包下的 可以抛出异常,可以有返回值 (知道就行)
二:关于线程对象的生命周期
新生状态 : new的时候,线程处于新生状态
就绪状态 : start()线程就会进入到就绪状态,线程就会进入到就绪队列进行等 待,等待cpu的调度
运行状态 : cpu把资源分配给这个线程,线程才能进入运行状态
阻塞状态 : sleep()可以让线程进入阻塞
终止状态 : 线程结束
注意:
一个线程一旦进入阻塞状态,阻塞接触 无法直接恢复到运行状态,会直接进入就绪状态
如果一个线程一旦终止,无法恢复,重新创建也不是刚刚的线程
进入终止状态的情况
1. stop destroy() 不推荐使用
2. 通过标识判断–推荐
3. 线程正常执行完毕
进入就绪状态的情况
-
start()
-
阻塞解除
-
线程切换
-
yield() 礼让线程 让出CPU资源 , 原线程直接 进入就绪 状态
/* - 礼让线程 yield --> 高风亮节 - 让出cpu资源 , 原线程直接 进入就绪 状态 */ public class YieldDemo04 implements Runnable{ public static void main(String[] args) { YieldDemo04 y = new YieldDemo04(); new Thread(y,"A").start(); new Thread(y,"B").start(); } @Override public void run() { System.out.println(Thread.currentThread().getName()+"开始----------------------"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"结束----------------------"); } }
进入阻塞状态的情况
-
sleep()
-
wait()
-
join()
/* join() 插队线程|加入 插队之前先就绪 */ public class JoinDemo06 implements Runnable{ public static void main(String[] args) { Thread th = new Thread(new JoinDemo06()); for(int i = 1;i<=10;i++) { System.out.println("main线程执行--------------"+i); if(i==5){ th.start(); //就绪插队 try { th.join(1000); //th线程插队到当前主线程中,主线程等待th线程结束,才能继续执行 } catch (InterruptedException e) { e.printStackTrace(); } //插队 } } } @Override public void run() { System.out.println("--------------------A线程开始执行--------------------"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("--------------------A线程结束执行--------------------"); } }
sleep(ms) : 睡眠
在sleep休眠时间过程当中,当前会让出CPU的资源, 当休眠时间解除,恢复到就绪,可供CPU选择抱着资源睡觉,—> 对象资源(对象的锁),不会释放锁
1. 网络延迟
2. 放大问题的可能性
示例:
/* 根据标识做判断,控制线程的结束 龟兔赛跑 兔子每跑10步休息 , 乌龟不休息, 有人赢了就结束, 跑100步就赢了 */ public class Race02 implements Runnable{ //标识 记录赢了人的姓名 private String winner; @Override public void run() { //i控制步数 for(int i =1;i<=100;i++) { // 兔子 10步 System.out.println(Thread.currentThread().getName()+"正在跑第"+i+"步"); if("兔子".equals(Thread.currentThread().getName()) && i%10==0) { try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } //判断是否要结束方法 if(!checkOver(i)) { break; } } } //判断是否结束线程 返回值false结束线程 true继续跑步 public boolean checkOver(int steps) { //是否已经有人赢了, 结束 if(winner != null) { return false; } //没有人赢了,判断我自己赢没赢 if(steps == 100) { winner = Thread.currentThread().getName(); return false; } return true; } public static void main(String[] args) { Race02 race = new Race02(); new Thread(race,"兔子").start(); new Thread(race,"乌龟").start(); } }
三.线程的调度
1.常见的线程调度模型有哪些?
抢占式调度模型:
那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
java采用的就是抢占式调度模型。
均分式调度模型:
平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
平均分配,一切平等。有一些编程语言,线程调度模型采用的是这种方式。
2.java中提供了哪些方法是和线程调度有关系的呢?
实例方法: void setPriority(int newPriority) 设置线程的优先级 int getPriority() 获取线程优先级 最低优先级1 默认优先级是5 最高优先级10 优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。) 静态方法: static void yield() 让位方法 暂停当前正在执行的线程对象,并执行其他线程 yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。 yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。 注意:在回到就绪之后,有可能还会再次抢到。 实例方法: void join() 合并线程 class MyThread1 extends Thread { public void doSome(){ MyThread2 t = new MyThread2(); t.join(); // 当前线程进入阻塞,t线程执行,直到t线 程结束。当前线程才可以继续。 } } class MyThread2 extends Thread{ }
四.关于多线程并发环境下,数据的安全问题
以后在开发中,我们的项目都是运行在服务器当中,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现完了。这些代码我们都不需要编写。
最重要的是:你要知道,你编写的程序需要放到一个多线程的环境下运行,你更需要关注的是这些数据在多线程并发的环境下是否是安全的。
1.线程同步机制与异步机制:
异步编程模型:
线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,
谁也不需要等谁,这种编程模型叫做:异步编程模型。
其实就是:多线程并发(效率较高。)
异步就是并发。
同步编程模型:
线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行
结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,
两个线程之间发生了等待关系,这就是同步编程模型。
效率较低。线程排队执行。
同步就是排队。
2.Java中有三大变量?【重要的内容。】
实例变量:在堆中。 静态变量:在方法区。 局部变量:在栈中。 以上三大变量中: 局部变量永远都不会存在线程安全问题。 因为局部变量不共享。(一个线程一个栈。) 局部变量在栈中。所以局部变量永远都不会共享。 实例变量在堆中,堆只有1个。 静态变量在方法区中,方法区只有1个。 堆和方法区都是多线程共享的,所以可能存在线程安全问题。 局部变量+常量:不会有线程安全问题。 成员变量:可能会有线程安全问题。
如果使用局部变量的话:建议使用:StringBuilder。
因为局部变量不存在线程安全问题。选择StringBuilder。
StringBuffer效率比较低。
ArrayList是非线程安全的。
Vector是线程安全的。
HashMap HashSet是非线程安全的。
Hashtable是线程安全的。
如何控制线程安全问题:
synchronized 关键字 控制线程安全–> 控制多个线程排队执行
用法:
1.同步方法 : 简单 效率较低
成员方法
静态方法
2.同步块
synchronized(this|类.class| 资源(成员属性)){ --> this 类 资源(成员属性)要被你同步控制的代码区域–>有可能 会出现问题的代码区域}double check 双重检查提高效率
注意:
同步一定要同步不变的东西,会变的锁不住,对象地址肯定不会变
同步的代码范围过大,效率低,代码范围小,锁不住
静态方法时属于 类的,同步静态方法,相当于同步类.class -->(类的class对象每一个类只有一个,不用你创建,在类加载到内存中时就已经存在)
public class SynDemo01 { public static void main(String[] args) { new Thread(()->{ System.out.println(SingleTon.newInstance()); }).start(); new Thread(()->{ System.out.println(SingleTon.newInstance()); }).start(); new Thread(()->{ System.out.println(SingleTon.newInstance()); }).start(); } } //懒汉式 class SingleTon{ //2私有的静态的该类的引用 private static SingleTon singleTon; //1.私有的构造器 private SingleTon(){} //A B C //3.公共的静态的访问方式,返回该类实例 public static SingleTon newInstance() { System.out.println("-----------------"); if(singleTon == null) { System.out.println("-----满足打印的话-----------"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //双重检查 double check -->提高效率 synchronized (SingleTon.class) { //同步类 if(singleTon == null) { singleTon = new SingleTon(); } } } return singleTon; }
public static SingleTon newInstance() { System.out.println("-----------------"); synchronized (SingleTon.class) { //同步类 if(singleTon == null) { System.out.println("-----满足打印的话-----------"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } singleTon = new SingleTon(); } } return singleTon; }
//同步方法 public static synchronized SingleTon newInstance() { if(singleTon == null) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } singleTon = new SingleTon(); } return singleTon; }
借鉴自:https://blog.csdn.net/Yuan_Xiao_Mu/article/details/107291722
更加全面的总结:https://blog.csdn.net/ChrisLu777/article/details/106587065