进程调度的基本过程
学不懂多线程?代码加实例详解帮你深入理解多线程(1)
上一篇文章主要介绍了进程和线程的概念、单线程与多线程的执行方式以及实现多线程的一种方法,今天这篇文章继续介绍另一种实现多线程的方法,以及线程调度过程中可能会用到的几种方法;
还记得上一篇文章末尾留个一个小问题吗? 输出结果为什么不是一个主栈一个支栈交替输出的呢?
要解答这个问题,我们首先要了解多线程的生命周期,也就是多个线程从被创建到执行结束这个过程是怎么样的;
一个进程在系统中的状态可以分为三种:就绪状态、运行状态和阻塞状态;
就绪状态:当start方法被调用时,进程进入就绪状态,标志着该进程可以抢夺控制台执行权(CPU时间片的执行时间),抢夺到执行权后即进入运行状态;
运行状态:run方法被调用后标志着这个进程进入运行状态,当抢夺到的时间片时间用完后,再次进入就绪状态抢夺时间,直到run方法运行结束,该进程生命周期结束;
阻塞状态:当一个进程遇到阻塞事件,例如用户键盘输入或sleep方法,会进入阻塞状态,并放弃时间片;
这也就可以解释为什么主线程和分支线程输出结果不是交替的了,这是因为它们抢夺到时间片的次数和时间的长短不一样;
首先,创建一个类实现Runnable接口;
class MyRunnable implements Runnable{
@Override
public void run(){
for(int i=0;i<100;i++){
System.out.println("这是一个分支栈——》"+i);
}
}
}
接着创建线程对象t;
//创建一个可运行对象
MyRunnable r = new MyRunnable();
然后再将可运行对象封装成一个线程对象;
//将可运行对象封装成一个线程对象
Thread t =new Thread(r);
最后启动线程;
//启动线程
t.start();
for(int i=0;i<100;i++){
System.out.println("这是一个主栈——》"+i);
}
完整代码如下:
public class myThread1 {
public static void main(String[] args) {
//创建一个可运行对象
MyRunnable r = new MyRunnable();
//将可运行对象封装成一个线程对象
Thread t =new Thread(r);
//启动线程
t.start();
for(int i=0;i<100;i++){
System.out.println("这是一个主栈——》"+i);
}
}
}
class MyRunnable implements Runnable{
@Override
public void run(){
for(int i=0;i<100;i++){
System.out.println("这是一个分支栈——》"+i);
}
}
}
输出结果如下:
第二种更好,第二种方式是面向接口编程,不影响继承其它类,更加灵活;
首先,创建一个匿名内部类;
Thread t= new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("t线程——》"+i);
}
}
});
启动线程
//启动线程
t.start();
此时分支栈开始运行,主栈继续向下运行;
//启动线程
t.start();
for(int i=0;i<100;i++){
System.out.println("main线程——》"+i);
}
输出结果为:
我们使用getName函数来获取线程对象的名字;
在上面的代码中插入getName函数;
public class myThread1 {
public static void main(String[] args) {
//创建一个可运行对象
MyRunnable r = new MyRunnable();
//将可运行对象封装成一个线程对象
Thread t =new Thread(r);
//获取线程对象的名字
String tName= t.getName();
//输出线程对象的名字
System.out.println(tName);
//启动线程
t.start();
for(int i=0;i<100;i++){
System.out.println("这是一个主栈——》"+i);
}
}
}
class MyRunnable implements Runnable{
@Override
public void run(){
for(int i=0;i<100;i++){
System.out.println("这是一个分支栈——》"+i);
}
}
}
获取到线程对象 t 的名字为:
获取当前线程的名字,我们使用currentThread方法;
Thread currentthread = Thread.currentThread();
将此语句插入main方法,即可获取主线程的名字;将此语句插入分支线程的方法中,即可获取分支线程的名字;
代码如下:
import java.util.Random;
public class testdemo {
public static void main(String[] args) {
System.out.println("当前线程的名字为:"+currentThread.getName());
mythread.start();
}
}
class myThread extends Thread{
@Override
public void run(){
}
}
输出结果为:
这里获取的是main线程的名字,并非实例化对象mythread的名字;
如果在run方法内部输出线程名字,会输出什么呢?
代码如下:
class myThread extends Thread{
@Override
public void run(){
Thread currentThread =Thread.currentThread();
System.out.println(currentThread.getName());
}
}
然后在main方法中实例化线程对象,代码如下:
public class testdemo {
public static void main(String[] args) {
//实例化对象
myThread thread0 = new myThread();
myThread thread1 = new myThread();
myThread thread2 = new myThread();
thread0.start();
thread1.start();
thread2.start();
}
}
输出结果为:
修改线程名字,使用的语句是:对象名.setName(“修改后的线程名字”)
;
代码如下:
import java.util.Random;
public class testdemo {
public static void main(String[] args) {
//实例化对象
myThread mythread = new myThread();
//获取线程对象的名字
System.out.println("修改前的名字:"+mythread.getName());
//修改线程对象的名字
mythread.setName("1234");
//输出修改后的名字
System.out.println("修改后的名字:"+mythread.getName());
mythread.start();
}
}
class myThread extends Thread{
@Override
public void run(){
}
}
输出结果为:
我们也可以试着修改一下主线程的名字,代码如下:
import java.util.Random;
public class testdemo {
public static void main(String[] args) {
//实例化对象
myThread mythread = new myThread();
Thread currentThread =Thread.currentThread();
System.out.println("当前线程的名字为:"+currentThread.getName());
//修改主线程的名字
currentThread.setName("主线程");
System.out.println("当前线程的名字为:"+currentThread.getName());
//获取线程对象的名字
System.out.println("修改前的名字:"+mythread.getName());
//修改线程对象的名字
mythread.setName("1234");
//输出修改后的名字
System.out.println("修改后的名字:"+mythread.getName());
mythread.start();
}
}
class myThread extends Thread{
@Override
public void run(){
}
}
输出结果为:
sleep方法可以使线程进入休眠,之后进入“阻塞状态”,放弃时间片占有;
在下面这段代码中,我们让线程休眠5秒后输出hello world;
public class myThread3 {
public static void main(String[] args) throws InterruptedException {
Thread.sleep(1000*5);
System.out.println("hello world!");
}
}
经过了5秒钟后,输出了helloworld;
如果一个线程休眠时间过长,我们想提前结束它的休眠,可以使用异常处理机制终止线程休眠;
代码如下:
public class MyThread5 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new MyRunnable3());
t.start();
Thread.sleep(1000*5);
t.interrupt();
}
}
class MyRunnable3 implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+"--->开始休眠");
try {
Thread.sleep(1000*60*60*24);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->休眠结束");
}
}
输出结果为:
interrupt是利用try catch方法跳过线程休眠的,需要注意的是,使用interrupt加try catch方法会利用异常处理机制跳过 Thread.sleep(1000*60*60*24)
语句,但并不影响后续代码的执行;
用代码验证一下:
public class myThread3 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new MyRunnable1());
t.start();
Thread.sleep(1000*5);
t.interrupt();
}
}
class MyRunnable1 implements Runnable{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出结果为:
我们可以用stop方法终止线程执行,但是stop方法是强行终止线程执行,可能会造成数据丢失,非常的不安全,所以不建议使用,仅作了解即可;
代码如下:
public class myThread3 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new MyRunnable1());
t.start();
Thread.sleep(1000*5);
t.stop();
}
}
class MyRunnable1 implements Runnable{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
使用布尔标记法,可以更安全地结束一个线程;
代码如下:
public class MyThread4 {
public static void main(String[] args) {
MyRunnable2 m= new MyRunnable2();
Thread t = new Thread(m);
t.setName("t");
t.start();
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
m.a=false;
}
}
class MyRunnable2 implements Runnable{
boolean a=true;
@Override
public void run(){
for(int i=0;i<10;i++){
if(a){
System.out.println(Thread.currentThread().getName()+"--->"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
return;
}
}
}
}
这是我的多线程讲解系列文章之二,如果您觉得有帮助的话,不妨来个点赞收藏加关注啊,您的关注与支持是我继续创作地动力!