大家好,欢迎来到IT知识分享网。
所谓多线程,就是说一个应用程序有多条执行路径,每当我们打开一个应用程序的时候,就像当与打开了一个进程,而进程中执行的操作,就是线程。以迅雷为例,打开迅雷就相当于打开一个进程,下载文件的操作就是线程,多线程就是同时下载多个文件
在Java中有两种方式实现多线程分别是继承Thread类和实现Runnable接口
目录
一、继承Thread类
Thread类中有一个run方法,但是这个run方法需要我们重写,故我们需要自定义一个类来继承Thread类,然后在自定义的类中重写这个run方法,重写完了就可以创建一个对象了,用该对象去调用这个方法,但是由于这是多线程,如果是直接调用run方法,无法实现多线程,然后就有了一个新的的方法start方法供我们使用,此方法会自动调用run方法。
程序代码:
package com.hezhiying;
public class Main {
public static void main(String[] args) {
//创建多线程对象
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
//设置每个对象的名字
mt1.setName("hello");
mt2.setName("world");
mt3.setName("java");
//调用start()方法,其内部调用了run()方法,实现了多线程
//在这里直接调用run()方法,不能实现多线程
mt1.start();
mt2.start();
mt3.start();
}
}
class MyThread extends Thread {
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(getName() + ":" + x);
}
}
}
运行结果:
由结果可以看出,在跑这个循环的时候,并不是mt1跑完再跑mt2,而是抢着跑的,这就是多线程的体现,也就是抢
二、实现Runnable接口
为了解决单继承的局限性,因为如果某个类已经有父亲了,那么它就不能再继承Thread来实现多线程了,所以就有了实现接口的多线程,我们大部分使用的方法也是用接口来实现多线程。接口方式实现多线程我们需要自定义一个类去实现Runnable接口,然后在自定义的类中重写run方法,再然后去创建一个自定义类的对象,最后创建一个Thread类的对象,并且把刚刚创建的自定义对象作为参数传递
程序代码:
package com.hezhiying;
public class Main {
public static void main(String[] args) {
//在这里只需创造一个接口子类对象,后面直接用mr就行了
MyRunnable mr = new MyRunnable();
// Thread t1 = new Thread(mr);
// Thread t2 = new Thread(mr);
// t1.setName("hello");
// t2.setName("java");
//上面这种方法不是很常用,可以用下面这个
Thread t1 = new Thread(mr,"hello");
Thread t2 = new Thread(mr,"java");
t1.start();
t2.start();
}
}
class MyRunnable implements Runnable {
public void run() {
for (int x = 0; x < 100; x++) {
//因为Runnable中没有getName方法,故用Thread中方法获取当前线程对象来实现
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
运行结果:
三、继承Thread类和实现Runnable方法的区别
- 继承Thread类不适合资源的共享,而实现Runnable接口很容易实现资源的共享
- 实现Runnable接口适合多个相同的程序代码的线程去处理同一个资源
- 避免了Java中的单继承限制
- 增强了程序的健壮性,代码可以被多个线程共享
四、线程中断interrupt()
程序代码:
package com.hezhiying;
public class Main {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
try {
Thread.sleep(1000);
mt.interrupt();// 这里的意思是线程执行如果超过1秒就被中断
} catch (InterruptedException e) {
// e.printStackTrace();
System.out.println("中断线程");
}
}
}
class MyThread extends Thread {
public void run() {
System.out.println("线程开始");
try {
// 在这里是线程休息2秒,如果线程在2秒内被中断,则执行catch
Thread.sleep(2000);
} catch (InterruptedException e) {
// e.printStackTrace();
System.out.println("线程中断");
}
System.out.println("线程结束");
}
}
运行结果:
五、线程同步synchronized()
多线程中,线程同步问题,为了讲解此问题,举一个火车卖票的例子,假如有3个窗口卖100张票,然后消费者去买票,当然了是三个窗口同时卖票,模拟一下此过程
package com.hezhiying;
public class Main {
public static void main(String[] args) {
// 创建一个MyRunnable类的对象,由于此对象没有继承Thread类,所以下面还需创建Thread对象
MyRunnable mr = new MyRunnable();
// 用此对象去创建Thread对象,并且给每个对象赋值,也就是起个名子
Thread t1 = new Thread(mr, "hello");
Thread t2 = new Thread(mr, "world");
Thread t3 = new Thread(mr, "java");
// 这里是启动线程的意思
t1.start();
t2.start();
t3.start();
}
}
/*
* 自定义一个类来实现Runnable接口,并重写此接口中的run方法
*/
class MyRunnable implements Runnable {
private int t = 100;
@Override
public void run() {
// 为了演示多线程的同步问题,在多线程同步中,
// 可能出现1号窗口还正在卖,而还没卖出去的时候,二号窗口刚好开始卖,导致一张票卖了两次
// 所以在这里我加上在卖票时候的一个延迟
while (t > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 这里调用的是当前线程的名字
System.out.println(Thread.currentThread().getName() + ":" + t--);
}
}
}
运行结果:
从上面的结果很容易看出,第100张票卖了3次,这肯定是不符合实际的,所以我们需要解决此问题,就是在某个窗口卖该张票的时候,其他窗口不能卖,也就是对该票进行加锁,即用synchronized()方法,其中有两种方式实现同步,分别是同步代码块和同步方法
package com.hezhiying;
public class Main {
public static void main(String[] args) {
// 创建一个MyRunnable类的对象,由于此对象没有继承Thread类,所以下面还需创建Thread对象
MyRunnable mr = new MyRunnable();
// 用此对象去创建Thread对象,并且给每个对象赋值,也就是起个名子
Thread t1 = new Thread(mr, "hello");
Thread t2 = new Thread(mr, "world");
Thread t3 = new Thread(mr, "java");
// 这里是启动线程的意思
t1.start();
t2.start();
t3.start();
}
}
/*
* 自定义一个类来实现Runnable接口,并重写此接口中的run方法
*/
class MyRunnable implements Runnable {
private int t = 100;
// 为了演示多线程的同步问题,在多线程同步中,
// 可能出现1号窗口还正在卖,而还没卖出去的时候,二号窗口刚好开始卖,导致一张票卖了两次
// 所以在这里我加上在卖票时候的一个延迟
// 方法一同步代码块
// public void run() {
// while (t > 0) {
// 同步对象的锁是this
// synchronized (this) {
// if (t > 0) {// 在这里可能出现在t--之前进来消费者,故加个判断
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// // 这里调用的是当前线程的名字
// System.out.println(Thread.currentThread().getName() + ":" + t--);
// }
// }
// }
// }
// 方法二同步方法
public void run() {
while (t > 0) {
sellt();
}
}
// 此方法为同步方法
private synchronized void sellt() {
if (t > 0) {// 在这里可能出现在t--之前进来消费者,故加个判断
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 这里调用的是当前线程的名字
System.out.println(Thread.currentThread().getName() + ":" + t--);
}
}
}
运行结果:
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/21788.html