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

继承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:10
false

主线程在执行到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:10
false

实现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:2
Thread-1:2
Thread-0:4
Thread-1:4
Thread-0:6
Thread-1:6
Thread-0:8
Thread-0:10
Thread-1:8
Thread-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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值