线程安全:
单线程操作,没有并发,一般是早期版本方法
StringBuffer
Vector
HashTable
线程非安全:
多线程并发,一般是新版本方法,提高了效率,但是带来了多线程的问题。
StringBuilder
ArrayList
HashMap
线程运行顺序主要依靠电脑CPU自动进行分配,我们只能用一些方法来间接控制一些特殊情况,主要还是靠CPU自己分配资源。
线程的状态:
创建线程(线程存在) - 就绪状态(可以执行,等待CPU分配资源,获得资源后开始运行) - 执行状态(运行线程,开始工作执行代码) - 等待/挂起(cpu给的资源用完了,或者我们停了它以便执行其他线程,等待结束后回到就绪状态) - 异常/死亡(出现exception,或者停止)
创建线程: new Thread
就绪状态:start()
执行状态:run() //cpu自动执行,如果手动执行则会先把它运行完后再执行其他的线程(变成单线程)
等待/挂起:wait() ---》 notify,notifyAll 方法唤醒后返回就绪状态
异常/死亡:抛出异常Exception,或者人为强制结束(以前用stop,现在已经过时了),线程结束
线程的实现:
定义一个类
继承Thread类(java中的线程类)
重写run方法
new 线程对象(创建线程),进入就绪状态。
然后就等着CPU有时间了来分配资源(有时候也有高大上的叫时间碎片一类的=。=)处理它。
然后等他自动运行完或者我们用一些方法比如sleep一类的控制它的运行。
这里只介绍基础实现
package thread; public class ThreadTest { public static void main(String[] args){ //首先,下面三个例子不要同时运行,会9个线程并发看不清效果 //每次3个线程一起运行就可以了 //用到的class:Sporter //创建线程 Sporter s1 = new Sporter("博尔特"); Sporter s2 = new Sporter("刘翔"); Sporter s3 = new Sporter("苏炳添"); //就绪状态 s1.start(); s2.start(); s3.start(); //执行状态 //线程的执行由CPU进行管理,我们不能直接控制,CPU有资源后会进行分配并让线程执行。 //博尔特跑到第0米了 //博尔特跑到第1米了 //博尔特跑到第2米了 //博尔特跑到第3米了 //博尔特跑到第4米了 //博尔特跑到第5米了 //博尔特跑到第6米了 //博尔特跑到第7米了 //博尔特跑到第8米了 //博尔特跑到第9米了 //博尔特跑到第10米了 //博尔特跑到第11米了 //博尔特跑到第12米了 //博尔特跑到第13米了 //博尔特跑到第14米了 //刘翔跑到第0米了 //刘翔跑到第1米了 //刘翔跑到第2米了 //... //根据电脑性能结果不同(电脑越好的连续执行一个对象的次数越多),同一台电脑每次执行结果也不同。 //通过结果可以知道CPU在执行完一个线程的一部分后会执行其他线程,多线程交错运行 //-。-我之前一直以为多线程就是多个线程一起执行。。。现在看来只是逻辑上多线程,其实还是一根线。。。 System.out.println("-------------------------我是华丽的分界线1---------------------------------"); //用到的class:Runner //创建线程 Runner r1 = new Runner("博尔特"); Runner r2 = new Runner("刘翔"); Runner r3 = new Runner("苏炳添"); Thread t1 = new Thread(r1);//start等方法都是Thread中的,Runnable接口中没有,所以需要把对象通过Thread的构造方法变成Thread线程 Thread t2 = new Thread(r2); Thread t3 = new Thread(r3); //就绪状态 t1.start(); t2.start(); t3.start(); //执行状态 //执行结果与直接继承Thread类的方法创建的线程类似。 System.out.println("-------------------------我是华丽的分界线2---------------------------------"); //12306火车票售票系统 //用到的class:TicketWindow,System12306,Ticket TicketWindow tw1 = new TicketWindow("售票窗口1"); TicketWindow tw2 = new TicketWindow("售票窗口2"); TicketWindow tw3 = new TicketWindow("售票窗口3"); tw1.start(); tw2.start(); tw3.start(); //只截取了最后几行 //售票窗口3售出了一张:从ShangHai51到BeiJing51价格为400.0的车票 //售票窗口1售出了一张:从ShangHai93到BeiJing93价格为400.0的车票 //售票窗口2售出了一张:从ShangHai92到BeiJing92价格为400.0的车票 //售票窗口1售出了一张:从ShangHai95到BeiJing95价格为400.0的车票 //售票窗口3售出了一张:从ShangHai94到BeiJing94价格为400.0的车票 //售票窗口1售出了一张:从ShangHai97到BeiJing97价格为400.0的车票 //售票窗口2售出了一张:从ShangHai96到BeiJing96价格为400.0的车票 //售票窗口1售出了一张:从ShangHai99到BeiJing99价格为400.0的车票 //售票窗口3售出了一张:从ShangHai98到BeiJing98价格为400.0的车票 //对不起,售票窗口3票已售完 //对不起,售票窗口2票已售完 //对不起,售票窗口1票已售完 } }
class Sporter
package thread; public class Sporter extends Thread { private String name; public Sporter(){} public Sporter(String name){ this.name = name; } public void run(){ //重写run方法 for(int i = 0; i < 100; i++) { //用来显示线程进程 System.out.println( name + "跑到第" + i + "米了"); } } }
class Runner
package thread; public class Runner implements Runnable { //Runnable 是个接口,里面只有run一个抽象方法,主要作用是告诉程序这个类可以作为线程,类似于Serilizable,这样这个类就可以继承其他类了(java每个类只能有一个父类,但是可以继承多个接口) private String name; public Runner(){} public Runner(String name){ this.name = name; } public void run(){ //重写run方法 for(int i = 0; i < 100; i++) { //用来显示线程进程 System.out.println( name + "跑到第" + i + "米了"); } } }
class System12306
package thread; import java.util.ArrayList; import java.util.Vector; //表示12306系统 //因为系统唯一,所以用单例模式设计 //单例模式的笔记:https://www.cnblogs.com/clamp7724/p/11604422.html public class System12306 { //单例模式,全世界只有这一个系统,可以直接调 // 这里假设只有这一个车次,真实情况肯定更复杂一些 private System12306(){} //构造函数私有化。 private static System12306 sys = new System12306();//所有外部类只能操作这一个对象。 public static System12306 getInstance(){ //给一个接口可以用外部创建对象访问这个类(不管创建多少个访问的其实是同一个对象,达到单例模式效果) return sys; } //存票用的数组(容器) private Vector<Ticket> tickerts = new Vector<>(); //因为对象只有一个所以static加不加都行。 //Vector方法自带synchronized(同步:两个地方不能同时运行这个对象,虽然修饰的是方法,但是锁定的是对象),是ArrayList的早期版本,线程安全,效率较低。 { //块,会在构造函数运行前运行,大学学的是在所有构造方法里加一遍。。。 for(int i = 0; i < 100; i++){ //假设这次有100张票 tickerts.add(new Ticket("ShangHai" + i, "BeiJing" + i, 400.0f));//加个i方便观察 } } //出票 public Ticket getTickert() { try { return tickerts.remove(0);//每次把链表第一个票删掉并返回 }catch (Exception e){ //如果没票了,可能会抛出异常,这时返回null。 也可以在前面加个if判断,if(tickerts.size() == 0){return null} return null; } } }
class TicketWindow
package thread; //多个窗口卖火车票用来演示多线程 public class TicketWindow extends Thread{//需要继承Thread类 private String windowName; public TicketWindow(String windowName){ this.windowName = windowName; } public void run(){ //重写run方法,用来控制运行的时候干什么 sellTicket(); //运行的时候不停卖票 } public void sellTicket(){ while(true){ System12306 s = System12306.getInstance();//获取12306对象 Ticket t = s.getTickert();//因为是Vector线程安全,所以不用加锁一类的防止多线程冲突。 if(t == null){ System.out.println("对不起," + windowName + "票已售完"); break; } else{ System.out.println(windowName + "售出了一张:" + t); //重写了toString方法,所以可以直接输出对象 t 的内容 } } } public String getWindowName() { return windowName; } public void setWindowName(String windowName) { this.windowName = windowName; } }
class Ticket
package thread; //用来储存火车票的信息 public class Ticket { private String startStation; private String endStation; private Float price = null; //补充:如果一个类只是拿来当一个容器,并没有特殊作用(只有属性和一些简单的方法,一般拿来对应储存数据库表中的数据),叫POJO类或者JavaBean。 //- -之前网上各种说JavaBean什么的,其实就是这种类。。。 //而数据库中有可能出现null值,如果拿float这种基本类型储存会报错,所以一般用对应的class来储存。class储存也可以直接赋值,而且程序健壮性更强。 //8个基本类: byte,short,int,long,float,double,boolean,char //8个基本类对应的class: Byte,Short,Integer,Long,Float,Double,Boolean,Character public Ticket(){ } public Ticket(String startStation, String endStation, Float price){ this.startStation = startStation; this.endStation = endStation; this.price = price; } public String toString(){ //重写toString方法,方便打印。 System.out.println()中直接写对象名时默认调用的是toString方法 return "从" + startStation + "到" + endStation + "价格为" + price + "的车票"; } //右键 --- Generate --- get and set ---选中要创建的属性,可以自动生成get和set方法 public String getStartStation() { return startStation; } public void setStartStation(String startStation) { this.startStation = startStation; } public String getEndStation() { return endStation; } public void setEndStation(String endStation) { this.endStation = endStation; } public Float getPrice() { return price; } public void setPrice(Float price) { this.price = price; } }