多线程基础1-线程2种方式、2方式实现购票、join、yield

来源:互联网 发布:java金融开源项目 编辑:程序博客网 时间:2024/06/06 21:38

      • 继承Thread创建线程
      • 实现Runnable创建线程
      • 继承Thread实现购票
      • 实现Runnable实现购票

继承Thread创建线程

package safly;/* * Thread的常用方法: * 1.start():启动线程并执行相应的run()方法 * 2.run():子线程要执行的代码放入run()方法中 * 3.currentThread():静态的,调取当前的线程 * 4.getName():获取此线程的名字 * 5.setName():设置此线程的名字 * 6.yield():调用此方法的线程释放当前CPU的执行权 * 7.join():在A线程中调用B线程的join()方法,表示:当执行到此方法,A线程停止执行,直至B线程执行完毕, * A线程再接着join()之后的代码执行 * 8.isAlive():判断当前线程是否还存活 * 9.sleep(long l):显式的让当前线程睡眠l毫秒 * 10.线程通信:wait()   notify()  notifyAll() *  * 设置线程的优先级 * getPriority() :返回线程优先值    setPriority(int newPriority) :改变线程的优先级 */class SubThread extends Thread {    public void run() {        for (int i = 1; i <= 10; i++) {         System.out.println(Thread.currentThread().getName() + ":"                    + Thread.currentThread().getPriority() + ":" + i);        }    }}public class Demo {    public static void main(String[] args) {        SubThread st1 = new SubThread();        st1.setName("子线程1");        st1.start();        Thread.currentThread().setName("========主线程");        for (int i = 1; i <= 10; i++) {            System.out.println(Thread.currentThread().getName() + ":"                    + Thread.currentThread().getPriority() + ":" + i);             if(i % 2 == 0){             Thread.currentThread().yield();             }             if(i == 7){             try {             st1.join();             } catch (InterruptedException e) {             // TODO Auto-generated catch block             e.printStackTrace();             }             }        }        System.out.println(st1.isAlive());    }}
========主线程:5:1子线程1:5:1========主线程:5:2子线程1:5:2========主线程:5:3子线程1:5:3========主线程:5:4子线程1:5:4子线程1:5:5========主线程:5:5========主线程:5:6子线程1:5:6========主线程:5:7子线程1:5:7子线程1:5:8子线程1:5:9子线程1:5:10========主线程:5:8========主线程:5:9========主线程:5:10false

主线程在执行到i=7时候,如果子线程任务没有执行完毕,那么子线程join进来,执行完毕子线程代码,在继续执行主线程代码,所以子线程执行任务完毕之后,才执行主线程i=8的操作

因为主线程在i=2时候,交出执行权限,然后主线程、子线程继续抢夺cpu执行权限,上面的例子就是主线程yield交出cpu执行权限,由子线程抢夺
下面的例子就是主线程在yield交出cpu执行权限,再次有主线程获取
或者如下的效果

========主线程:5:1========主线程:5:2========主线程:5:3========主线程:5:4子线程1:5:1子线程1:5:2子线程1:5:3子线程1:5:4========主线程:5:5子线程1:5:5子线程1:5:6子线程1:5:7子线程1:5:8子线程1:5:9子线程1:5:10========主线程:5:6========主线程:5:7========主线程:5:8========主线程:5:9========主线程:5:10false

实现Runnable创建线程

package safly;/* * 创建多线程的方式二:通过实现的方式 *  * 对比一下继承的方式 vs 实现的方式 * 1.联系:public class Thread implements Runnable * 2.哪个方式好?实现的方式优于继承的方式 *    why?  ① 避免了java单继承的局限性 *          ② 如果多个线程要操作同一份资源(或数据),更适合使用实现的方式 *///1.创建一个实现了Runnable接口的类class PrintNum1 implements Runnable {    //2.实现接口的抽象方法    public void run() {        // 子线程执行的代码        for (int i = 1; i <= 10; i++) {            if (i % 2 == 0) {                System.out.println(Thread.currentThread().getName() + ":" + i);            }        }    }}public class Demo {    public static void main(String[] args) {        //3.创建一个Runnable接口实现类的对象        PrintNum1 p = new PrintNum1();        //要想启动一个多线程,必须调用start()        //4.将此对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程        Thread t1 = new Thread(p);        //5.调用start()方法:启动线程并执行run()        t1.start();//启动线程;执行Thread对象生成时构造器形参的对象的run()方法。        //再创建一个线程        Thread t2 = new Thread(p);        t2.start();    }}

输出如下:

Thread-0:2Thread-1:2Thread-0:4Thread-1:4Thread-0:6Thread-1:6Thread-0:8Thread-0:10Thread-1:8Thread-1:10

继承Thread实现购票

package safly;//模拟火车站售票窗口,开启三个窗口售票,总票数为100张//存在线程的安全问题class Window extends Thread {    static int ticket = 10;    public void run() {        while (true) {            if (ticket > 0) {                System.out.println(Thread.currentThread().getName() + "售票,票号为:"                        + ticket--);            } else {                break;            }        }    }}public class Demo {    public static void main(String[] args) {        Window w1 = new Window();        Window w2 = new Window();        Window w3 = new Window();        w1.setName("窗口1");        w2.setName("窗口2");        w3.setName("窗口3");        w1.start();        w2.start();        w3.start();    }}

需要注意的是在run方法中,使用共享的静态变量,这个方法其实也不能完全解决数据共享的问题,因为可能某一个线程拿到火车票了,例如票号10,然后去执行–操作,在这个–操作还没执行完,另外一个线程也拿到共享数据了,此刻依然是没有进行减少操作完毕之前的10,所以就造成如下的结果,打印重票错票

窗口3售票,票号为:10窗口2售票,票号为:9窗口1售票,票号为:10窗口2售票,票号为:7窗口3售票,票号为:8窗口2售票,票号为:5窗口1售票,票号为:6窗口1售票,票号为:2窗口2售票,票号为:3窗口3售票,票号为:4窗口1售票,票号为:1

如果改成int ticket = 10;就是开了3个线程,每个线程有自己的变量,而非static共享的变量,所以每个线程会输出10长票
看看是如何输出的

窗口3售票,票号为:10窗口2售票,票号为:10窗口1售票,票号为:10窗口2售票,票号为:9窗口3售票,票号为:9窗口2售票,票号为:8窗口1售票,票号为:9窗口2售票,票号为:7窗口2售票,票号为:6窗口3售票,票号为:8窗口2售票,票号为:5窗口2售票,票号为:4窗口3售票,票号为:7窗口3售票,票号为:6窗口3售票,票号为:5窗口3售票,票号为:4窗口1售票,票号为:8窗口1售票,票号为:7窗口1售票,票号为:6窗口3售票,票号为:3窗口2售票,票号为:3窗口3售票,票号为:2窗口1售票,票号为:5窗口3售票,票号为:1窗口2售票,票号为:2窗口2售票,票号为:1窗口1售票,票号为:4窗口1售票,票号为:3窗口1售票,票号为:2窗口1售票,票号为:1

实现Runnable实现购票

package safly;//使用实现Runnable接口的方式,售票/* * 此程序存在线程的安全问题:打印车票时,会出现重票、错票 */class Window1 implements Runnable {    static int ticket = 10;    //int也可以//  int ticket = 10;    public void run() {        while (true) {            if (ticket > 0) {                System.out.println(Thread.currentThread().getName() + "售票,票号为:"                        + ticket--);            } else {                break;            }        }    }}public class Demo {    public static void main(String[] args) {        Window1 w = new Window1();        Thread t1 = new Thread(w);        Thread t2 = new Thread(w);        Thread t3 = new Thread(w);        t1.setName("窗口1");        t2.setName("窗口2");        t3.setName("窗口3");        t1.start();        t2.start();        t3.start();    }}

利用实现的方式实现购票,同理一样,但是static int ticket = 10;跟int ticket = 10;写法都行,但是都会出现重票错票
输出如下:

窗口1售票,票号为:10窗口3售票,票号为:9窗口3售票,票号为:7窗口2售票,票号为:9窗口3售票,票号为:6窗口3售票,票号为:4窗口1售票,票号为:8窗口3售票,票号为:3窗口3售票,票号为:1窗口2售票,票号为:5窗口1售票,票号为:2
阅读全文
0 0