Java多线程开发--线程常用操作方法
内容学习于:edu.aliyun.com
1. 线程的取名和获得
多线程的运行状态是不确定的,那么在程序的开发之中为了可以获取到一些需要使用到线程就只能够依靠线程的名字来进行操作。所以线程的名字是-一个至关重要的概念,这样在Thread类之中就提供有线程名称的处理:
- 构造方法:public Thread(Runnable target , String name);
- 取得名字: public final String getName()
- 设置名字:public final void setName(String name)
对于线程对象的获得是不可能只是依靠一个this来完成的,因为线程的状态不可控,但是有一点是明确的,所有的线程对象一定要执行run()方法,那么这个时候可以考虑获取当前线程,在Thread类里面提供有获取当前线程的方法:
- 获取当前线程:public static Thread currentThread()
代码:
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class Demo1 {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt, "线程A").start();//设置线程名字
new Thread(mt).start();//没有设置线程名字
new Thread(mt, "线程B").start();//设置线程名字
}
}
结果:
线程A
线程B
Thread-0
当开发者为线程设置名字的时候就使用设置的名字,而如果没有设置名字,则会自动生成一个不重复的名字,这种自动的属性命名主要是依靠了static属性完成的。
代码:
public class Demo1 {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt).start();//没有设置线程名字
new Thread(mt).start();//没有设置线程名字
new Thread(mt).start();//没有设置线程名字
new Thread(mt).start();//没有设置线程名字
new Thread(mt).start();//没有设置线程名字
}
}
结果:
Thread-0
Thread-3
Thread-4
Thread-1
Thread-2
自动命名原理:
在Thread类里面定义有如下操作:
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
代码:
//对象直接调用run()方法
public class Demo1 {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt,"线程对象").start();//设置线程名字
mt.run();//对象直接调用run()方法
}
}
观察下面代码:
public class JavaDemo
{
public static void main(String args[]){
for(int x= 0; x< Integer.MAX_VALUE;x++){
System.out.println();
}
}
}
进程如下图所示:
<mark>每当启动一个JVM就会启动一个进程,每个主方法都是主进程</mark>
<mark>在任何的开发之中,主线程可以创建若干个子线程,创建子线程的目的是可以将一些复杂逻辑或者比较耗时的逻辑交由子线程处理;</mark>
代码:
例子1:
public class Demo1 {
public static void main(String[] args) throws Exception {
System.out.println("1、执行任务一");
int temp = 0;
for (int x = 0; x < Integer.MAX_VALUE; x++) {
temp += x;
}
System.out.println("2、执行任务二");
System.out.println("n、执行任务N");
}
}
从结果上看,后续的任务会延迟出现
例子2:
//子线程处理
public class Demo1 {
public static void main(String[] args) throws Exception {
System.out.println("1、执行任务一");
new Thread(() -> {//子线程负责统计
int temp = 0;
for (int x = 0; x < Integer.MAX_VALUE; x++) {
temp += x;
}
}).start();
System.out.println("2、执行任务二");
System.out.println("n、执行任务N");
}
}
<mark>主线程负责处理整体流程,而子线程负责处理耗时操作</mark>,这样将不影响后续的任务进行。
2. 线程的休眠
如果说现在希望某一个线程可以暂缓执行,那么就可以使用休眠的处理,在Thread类之中定义的休眠方法如下:
- 休眠:public static void sleep(long millis) throws InterruptedException
- 休眠:public static void sleep(long millis,int nanos) throws InterruptedException
在进行休眠的时候有可能会产生中断异常“InterruptedException", 中断异常属于Exception 的子类,所以证明该异常必须进行处理。
线程休眠代码:
public class Demo1 {
public static void main(String[] args) throws Exception {
new Thread(() -> {
for (int x = 0; x < 10; x++) {
System.out.println(Thread.currentThread().getName() + "、x=" + x);
try {
Thread.sleep(1000);//暂缓执行
} catch (InterruptedException e) {//必须处理中断异常
e.printStackTrace();
}
}
}).start();
}
}
休眠的主要特点是可以自动实现线程的唤醒,以继续进行后续的处理。<mark>但是需要注意的是,如果现在有多个线程对象,那么休眠也是有先后顺序的。</mark>
产生多个多个对象进行休眠处理:
public class Demo1 {
public static void main(String[] args) throws Exception {
for (int num = 0; num < 5; num++) {
new Thread(() -> {
for (int x = 0; x < 10; x++) {
System.out.println(Thread.currentThread().getName() + "、x=" + x);
try {
Thread.sleep(1000);//暂缓执行
} catch (InterruptedException e) {//必须处理中断异常
e.printStackTrace();
}
}
}, "线程" + num).start();
}
}
}
此时将产生五个线程对象,并且这五个线程对象执行的方法体是相同的。此时从程序执行的感觉上来讲好像是若千个线程一起进行了休眠,而后一起进行了自动唤醒,但是实际上是有差别的。
如下图所示:
3. 线程中断
在之前发现线程的休眠里面提供有一个中断异常,实际上就证明线程的休眠是可以被打断的,而这种打断肯定是由其它线程完成的,在Thread类里面提供有这种中断执行的处理方法:
- 判断线程是否中断:public static boolean interrupted()
- 中断线程执行:public void interrupt()
线程中断代码:
public class Demo1 {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> {
System.out.println("***准备睡觉了***");
try {
Thread.sleep(10000);//准备睡10秒
} catch (InterruptedException e) {
System.out.println("【敢不让我睡觉???】");
}
System.out.println("睡够了!!!");
});
thread.start();//开始线程
Thread.sleep(1000);//先休眠1秒钟
if (!thread.isInterrupted()) {//判断线程是否中断执行
System.out.println("【我就轻轻的打扰一下】");
thread.interrupt();//中断线程
}
}
}
结果:
***准备睡觉了***
【我就轻轻的打扰一下】
【敢不让我睡觉???】
睡够了!!!
所有正在执行的线程都是可以被中断的,<mark>中断线程必须进行异常的处理。</mark>
4. 线程强制执行
所谓的线程的强制执行指的是当满足于某些条件之后,某一个线程对象将可以一直独占资源,一直到该线程的程序执行结束。
代码:
public class Demo1 {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> {
for (int x = 0; x < 3; x++) {
System.out.println(Thread.currentThread().getName() + "执行、x=" + x);
}
}, "玩耍的子线程");
thread.start();//开始线程
for (int x = 0; x < 3; x++) {
System.out.println("【霸道的主线程】执行、x=" + x);
}
}
}
结果:
【霸道的主线程】执行、x=0
【霸道的主线程】执行、x=1
【霸道的主线程】执行、x=2
玩耍的子线程执行、x=0
玩耍的子线程执行、x=1
玩耍的子线程执行、x=2
这个时候主线程和子线程都在交替执行着,但是如果说现在你希望主线程独占执行。那么就可以利用Thread类中的方法:
- 强制执行:public final void join() throws InterruptedException
代码:
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int x = 0; x < 5; x++) {
System.out.println(Thread.currentThread().getName() + "执行、x=" + x);
}
}, "玩耍的子线程");
thread.start();//开始线程
for (int x = 0; x < 5; x++) {
if(x==2){
thread.join();//子线程强行执行
}
System.out.println("【霸道的主线程】执行、x=" + x);
}
}
}
结果:
【霸道的主线程】执行、x=0
【霸道的主线程】执行、x=1
玩耍的子线程执行、x=0
玩耍的子线程执行、x=1
玩耍的子线程执行、x=2
玩耍的子线程执行、x=3
玩耍的子线程执行、x=4
【霸道的主线程】执行、x=2
【霸道的主线程】执行、x=3
【霸道的主线程】执行、x=4
<mark>在进行线程强制执行的时候一定要获取强制执行线程对象之后才可以执行join()调用。</mark>
5. 线程礼让
线程的礼让指的是先将资源让出去让别的线程先执行。线程的礼让可以使用Thread中提供的方法:
- 礼让:public static void yield()
代码:
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(() -> {
for (int x = 0; x < 10; x++) {
System.out.println(Thread.currentThread().getName() + "执行、x=" + x);
}
}, "玩耍的子线程");
Thread threadB = new Thread(() -> {
for (int x = 0; x < 10; x++) {
if (x % 3 == 0) {
System.out.println("【霸道的线程礼让了!!!】");
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "执行、x=" + x);
}
}, "霸道的子线程");
threadA.start();//开始线程
threadB.start();
}
}
结果:
【霸道的线程礼让了!!!】
玩耍的子线程执行、x=0
霸道的子线程执行、x=0
玩耍的子线程执行、x=1
霸道的子线程执行、x=1
玩耍的子线程执行、x=2
霸道的子线程执行、x=2
【霸道的线程礼让了!!!】
玩耍的子线程执行、x=3
霸道的子线程执行、x=3
玩耍的子线程执行、x=4
霸道的子线程执行、x=4
玩耍的子线程执行、x=5
霸道的子线程执行、x=5
【霸道的线程礼让了!!!】
玩耍的子线程执行、x=6
霸道的子线程执行、x=6
玩耍的子线程执行、x=7
霸道的子线程执行、x=7
玩耍的子线程执行、x=8
霸道的子线程执行、x=8
【霸道的线程礼让了!!!】
玩耍的子线程执行、x=9
霸道的子线程执行、x=9
<mark>礼让执行的时候每一次调用yield()方法都只会礼让一次当前的资</mark>源。.
6. 线程优先级
从理论上来讲线程的优先级越高越有可能先执行(越有可能先抢占到资源)。在Thread类里面针对于优先级的操作提供有如下的两个处理方法:
- 设置优先级:public final void setPriority(int newPriority)
- 获取优先级:public final int getPriority()
在进行优先级定义的时候都是通过int型的数字来完成的,而对于此数字的选择在Thread类里面就定义有三个常量:
- 最高优先级:public static final int MAX_PRIORITY --------10
- 中等优先级:public static final int NORM_PRIORITY --------5
- 最低优先级:public static final int MIN_PRIORITY ----------1
代码:
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Runnable run = () -> {
for (int x = 0; x < 3; x++) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread threadA = new Thread(run,"线程A");
Thread threadB = new Thread(run,"线程B");
Thread threadC = new Thread(run,"线程C");
//设置线程优先级
threadA.setPriority(Thread.MIN_PRIORITY);
threadB.setPriority(Thread.MIN_PRIORITY);
threadC.setPriority(Thread.MAX_PRIORITY);
threadA.start();
threadB.start();
threadC.start();
}
}
结果:
线程A
线程C
线程B线程A
线程C
线程B线程C
线程A
线程B
<mark>主方法是一个主线程</mark>,那么主线程的优先级呢?
代码:
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程优先级:"+Thread.currentThread().getPriority());
Thread threadA = new Thread(()->{},"线程A");
System.out.println("子线程优先级:"+threadA.getPriority());
}
}
结果:
主线程优先级:5
子线程优先级:5
<mark>主线程是属于中等优先级,而默认创建的线程也是中等优先级。</mark>