创建线程的其他知识点
前两篇博客我写了有关创建线程以及创建线程池的方法,这篇博客用来总结JAVA多线程实战第一章创建线程的其他内容,一些创建和使用线程的知识点。
一、Thread类和Runnable接口的区别
之前讲的创建线程的四种方法中前两中方法分别是通过继承Thread类和实现Runnable接口创建线程,这两种方法是创建线程的最常用方法,那两者有什么区别?
直接通过代码演示。
public class MyThread extends Thread{
private int ticket = 10;
public MyThread(String name) {
super(name);
}
@Override
public void run() {
while(true) {
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"卖出第"+(10-ticket--+1)+"张门票");
}
}
}
}
public class MyThreadTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread mt1 = new MyThread("窗口一");
MyThread mt2 = new MyThread("窗口二");
MyThread mt3 = new MyThread("窗口三");
mt1.start();
mt2.start();
mt3.start();
}
}
public class MyRunnable implements Runnable{
private int ticket = 10;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
if(ticket>0) {
System.out.println(Thread.currentThread().getName()+"卖出"+(10- ticket-- +1)+"张票");
}
}
}
}
public class MyRunnableTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr,"窗口一");
Thread t2 = new Thread(mr,"窗口二");
Thread t3 = new Thread(mr,"窗口三");
t1.start();
t2.start();
t3.start();
}
}
继承Thread类创建线程的运行结果是三个线程独立进行,不共享资源。所以可知,这种方法创建线程不共享资源
实现Runnable接口的运行结果是三个线程共享资源,可以把Runnable当成资源,多线程共享资源
二、线程start和run方法的区别
通过代码演示
public class UseThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+",执行run方法");
}
}
public class UserThreadTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()+" 执行main方法");
UseThread useThread = new UseThread();
useThread.run();//只有一个线程再执行,是单线程的
useThread.start();//创建一个新线程
}
}
根据运行结果可以看出,使用run()方法时,程序是单线程,只有主线程main在执行任务,run()方法的内容也是有主线程执行,并没有新建线程执行run()方法
使用start()方法后,run()方法里的内容被新建的线程执行。
所以如果想新建线程,那就要用start()方法
三、线程的优先级
JAVA默认情况下线程是有轮流使用CPU的特权,平均分配给每个线程占用CPU的使用时间,但是设置优先级后就不一样了
有关线程优先级的知识点
- Java的优先级是从1-10,默认优先级是5,10最高。
- 主线程main的优先级是5
- 优先级高的线程占用CPU的概率大,但是优先级低的线程并不是没有机会执行,只是概率问题
- 即使设置了优先级,也无法保证线程的执行顺序,只是概率问题
public class UserRunnable implements Runnable{
@Override
public void run() {
for(int i = 0;i<10;i++) {
System.out.println(Thread.currentThread().getName()+"第"+i+"次执行");
}
}
}
public class UserThread extends Thread{
@Override
public void run() {
for(int i = 0;i<10;i++) {
System.out.println(Thread.currentThread().getName()+"第"+i+"次执行");
}
}
}
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
UserThread ut = new UserThread();
UserRunnable ur = new UserRunnable();
Thread t = new Thread(ur);
ut.setPriority(1);
t.setPriority(10);
ut.start();
t.start();
}
}
四、定时线程的任务调度
调用定时线程有两种方法,分别是schedule()和scheduleAtFixedRate(),两者区别是前者特点为延时不追加执行任务,后者特点为延时追加任务。这两个特点的含义是什么呢?
延时不追加任务:当前系统时间已经超过了设置时间,线程从当前时间执行任务。当前时间没到设置时间,线程会等时间到了再执行任务
延时追加任务:当前系统时间已经超过了设置时间,线程会将当前时间和设置时间组差,根据设置的任务执行间隔算出少执行多少次人物,补充之前少的执行结果
五、接口同步回调和异步回调
- 同步调用:一种阻塞式调用,调用方要等待对方执行完才能返回,它是一种单向调用,平时我们调用方法就是同步调用
- 回调:一种双向调用模式,被调用方在接口被调用时也会调用对方接口
- 异步调用:一种类似事件或消息机制,不过他的调用方向刚好相反,接口的服务在接收到某种讯息或者发生某种事件会主动通知客户端。不需要等待对方执行完再返回
第一种情况
public interface Callback {
public void process(String msg);
}
public class MyCallback implements Callback{
@Override
public void process(String msg) {
// TODO Auto-generated method stub
System.out.println("处理成功,返回状态为: "+msg);
}
}//这个方法就是回调函数,客户端给服务端输入信息,通过回调函数告知客户端信息已被处理
public class Server {
public void getMsg(Callback callback,String msg) throws InterruptedException {
System.out.println("服务器端已接收到信息:"+msg);
//模拟消息处理,等待两分钟
Thread.sleep(2000);
System.out.println("消息已经被处理,返回状态为:"+msg);
//处理完消息,调用客户端,已经处理好消息
callback.process(msg);
}//获取服务器端消息并且对消息进行处理,再告诉客户端你的消息已经被处理成功了
}
public class Client {
Server server;
public Client(Server server) {
this.server = server;
}
public void sendMsg(String msg) throws InterruptedException {
System.out.println("客户端发出消息:"+msg);
server.getMsg(new MyCallback(), msg);
System.out.println("客户端已发出,等待处理");
}
}
public class Test1 {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Server s = new Server();
Client c = new Client(s);
c.sendMsg("200");
}
}
这种就是调用方法,也就是同步回调,输出结果如下:
客户端发出消息:200
服务器端已接收到信息:200
消息已经被处理,返回状态为:200
处理成功,返回状态为: 200
客户端已发出,等待处理
可以看出这个输出结果的顺序不是我们想要的,因为程序在等getMsg执行完才会往下执行
解决方法,在客户端创建线程是
public class Client {
Server server;
public Client(Server server) {
this.server = server;
}
public void sendMsg(String msg) throws InterruptedException {
System.out.println("客户端发出消息:"+msg);
// server.getMsg(new MyCallback(), msg);
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
server.getMsg(new MyCallback(), msg);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();;
System.out.println("客户端已发出,等待处理");
}
}
这次的执行结果就符合预期
客户端发出消息:200
客户端已发出,等待处理
服务器端已接收到信息:200
消息已经被处理,返回状态为:200
处理成功,返回状态为: 200
创建线程后就各干各的,不用等一个做完才能做下一个
六.总结
好啦,第一章全部总结完毕,希望能给其他学习java的伙伴带来一定关注,后续还会继续更新
- 点赞
- 收藏
- 关注作者
评论(0)