您的位置:首页 > 其它

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

2017-12-17 10:55 549 查看
继承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: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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐