目录
1.多线程的两种实现方式
2.两种实现方式的比较(转载)
3.多线程中常用的方法
4.多线程的应用:模拟车站售票
①继承Thread,重写run方法
②实现Runnable接口,重写run方法
package com.bdsw.wxl.day6.homework;
import org.junit.jupiter.api.Test;
/**
*
* @author 王雪亮
* @date 2018年7月21日
* @Description
* 多线程的2种实现方式:
* ①继承Thread,重写run方法
* ②实现Runnable接口,重写run方法
*/
public class TestThread {
@Test
public void test2()
{
Runnable1 r1=new Runnable1();
Thread t=new Thread(r1);
t.start();
r1.run();
}
//@Test
public void test1()
{
Thread1 t1=new Thread1();
t1.start(); //通过分线程调用run
t1.run(); //通过主线程调用run
}
}
/**
*
* @author 王雪亮
* @date 2018年7月21日
* @Description ①继承Thread
* 定义类继承Thread
* 重写run方法
* 把新线程要做的事写在run方法中
* 创建线程对象
* 开启新线程, 内部会自动执行run方法
*/
class Thread1 extends Thread{
@Override
public void run() {
int i=100;
for(i=1;i<101;)
{
System.out.println(Thread.currentThread().getName()+":"+i);
i+=2;
}
}
}
/**
*
* @author 王雪亮
* @date 2018年7月21日
* @Description ②实现Runnable
* 定义类实现Runnable接口
* 重写run方法(把新线程要做的事写在run方法中)
* 创建自定义类的对象
* 创建Thread对象, 传入自定义类的对象
* 调用start()开启新线程, 内部会自动调用Runnable的run()方法
*/
class Runnable1 implements Runnable{
@Override
public void run() {
int i=100;
for(i=0;i<101;)
{
System.out.println(Thread.currentThread().getName()+":"+i);
i+=2;
}
}
}
首先分析两种方式的输出结果,同样是创建了两个线程,为什么结果不一样呢?
使用实现Runnable接口方式创建线程可以共享同一个目标对象(TreadDemo1 tt=new TreadDemo1();),实现了多个相同线程处理同一份资源。当第一个线程执行完任务后,countDown已经为0,所以第二个线程就不会输出。而继承Thread创建线程的方式,new出了两个任务类对象,有各自的成员变量,相互之间不干扰。
然后再看一段来自JDK的解释:
Runnable
接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为run
的无参数方法。
设计该接口的目的是为希望在活动时执行代码的对象提供一个公共协议。例如,Thread
类实现了Runnable
。激活的意思是说某个线程已启动并且尚未停止。
此外,Runnable
为非 Thread
子类的类提供了一种激活方式。通过实例化某个Thread
实例并将自身作为运行目标,就可以运行实现Runnable
的类。大多数情况下,如果只想重写run()
方法,而不重写其他 Thread
方法,那么应使用Runnable
接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。(推荐使用创建任务类,并实现Runnable接口,而不是继承Thread类)
采用继承Thread类方式:
(1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
(2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。
采用实现Runnable接口方式:
(1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
(2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。
package com.bdsw.wxl.day6.homework;
import org.junit.jupiter.api.Test;
/**
*
* @author 王雪亮
* @date 2018年7月21日
* @Description
* 多线程常用的方法: start() run()
* currentThread():得到当前线程。
* setName(): 为线程设置一个名称。
* sleep(): 强迫一个线程睡眠N毫秒。
* isAlive(): 判断一个线程是否存活。
* join():等待线程终止。
* yield():调用此方法的线程释放当前cpu使用权
*
* 线程通信:wait(),notify(),notifyAll()
* wait():强迫一个线程等待。
* notify(): 通知一个线程继续运行。
*
* 设置线程的优先级:
* getPriority():返回线程的优先值
* setPriority(): 设置一个线程的优先级。
*/
class Thread2 extends Thread{
//打印100以内的奇数
public void printOdd()
{
int i=100;
for(i=1;i<101;)
{
System.out.println(Thread.currentThread().getName()+i);
i+=2;
}
}
@Override
public void run() {
printOdd();
Thread.currentThread().setPriority(MAX_PRIORITY);
System.out.println("~~子线程的优先级为:"+Thread.currentThread().getPriority());
}
}
public class TestThread2 {
@Test
public void test1() throws InterruptedException
{
Thread2 t1=new Thread2();
Thread mainThread=Thread.currentThread();
mainThread.setName("主线程:");
t1.setName("子线程1:");
t1.start();
System.out.println("~~子线程是否还存活:"+t1.isAlive());
mainThread.setPriority(1);
System.out.println("~~主线程的优先级为:"+mainThread.getPriority());
//打印100以内的奇数,当i==5时,将t1强行加入主线程,待t1线程执行完后,主线程继续执行
int i = 100;
for (i = 1; i < 101;) {
System.out.println(Thread.currentThread().getName() + i);
// if (i == 5)
// try {
// t1.join();// t1结束后,主线程才可以继续执行
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
i += 2;
}
}
}
需求:现车站开设了4个窗口,共销售100张车票,要求4个车站同时销售,请编写一个小程序,实现该功能。
思路:创建线程有两种方式,①继承Thread ②实现Runnable接口
可通过这两种方式实现上述功能
①继承Thread实现:
package com.bdsw.wxl.day6.homework;
public class Tickets2 extends Thread {
// 准备100张票
static int ticket = 100;
Object o=new Object();
//售票
@Override
public void run() {
while (ticket > 0) {
//此处加锁不能使用this或者随意构造一个对象,因为t1,t2,t3,t4
//都是Ticket2实例化的对象,当在Ticket2中定义一个对象时,t1~t4
//每个对象都会拥有一把自己的锁,故此时加锁等于没加,此时的解决
//方案可以直接使用类名.class
synchronized (Tickets2.class) {
System.out.println(Thread.currentThread().getName() + "已出售第" + ticket + "张票");
ticket--;
}
}
}
public static void main(String[] args) {
// 1.创建4个线程,充当4个窗口
Tickets2 t1 = new Tickets2();
Tickets2 t2 = new Tickets2();
Tickets2 t3 = new Tickets2();
Tickets2 t4 = new Tickets2();
// 2.为线程重命名
t1.setName("窗口1:");
t2.setName("窗口2:");
t3.setName("窗口3:");
t4.setName("窗口4:");
// 3.启动4个窗口
t1.start();
t2.start();
t3.start();
t4.start();
}
}
②实现Runnable接口实现
package com.bdsw.wxl.day6.homework;
public class Tickets implements Runnable {
// 准备100张票
int ticket = 100;
Object o=new Object();
// 售票
@Override
public void run() {
while (ticket > 0) {
synchronized(o) {
System.out.println(Thread.currentThread().getName() + "已出售第" + ticket + "张票");
ticket--;}
}
}
public static void main(String[] args) {
//1.创建一个Tickets对象
Tickets w1 = new Tickets();
//2.创建4个窗口
Thread t1=new Thread(w1,"窗口1:");
Thread t2=new Thread(w1,"窗口2:");
Thread t3=new Thread(w1,"窗口3");
Thread t4=new Thread(w1);
t4.setName("窗口4:");
//3.启动线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}