java - 线程基础

线程安全:

单线程操作,没有并发,一般是早期版本方法

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;
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

                                                                                                                                                                                                                                 

 

posted @ 2019-10-10 15:30  不咬人的兔子  阅读(157)  评论(0编辑  收藏  举报