Java中线程创建的两种方式(代码源)
1.继承自Thread类的方式
package cn.hp.thread1;
public class TestThread1 {
public static void main(String[] args) {
//创建线程对象
MyThread t1 = new MyThread();
//创建自定义对象
MyThread2 mythread = new MyThread2();
//创建Thread对象 把自定义对象当作参数传入
Thread t2 = new Thread(mythread);
//设置线程名称
t1.setName("我是t1线程");
//启动线程
t1.start();
t2.start();
for(int i=1;i<=100; i++){
System.out.println(i+"我是主线程");
}
}
}
//继承Thread类
class MyThread extends Thread{
//覆盖run()
public void run(){
//需要被线程执行的代码
for(int i=1;i<=100;i++){
System.out.println(i+this.getName());
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2.实现Runnable接口的方式
//实现Runnable接口
class MyThread2 implements Runnable{
public void run() {
//需要被线程执行的代码
for(int i=1;i<=100;i++){
System.out.println(i+"我是t2线程");
}
}
}
1
2
3
4
5
6
7
8
9
10
线程的生命周期
在线程的生命周期中,它要经过新建(New),就绪(Runnable),运行(Running),阻塞(Blocked)和死亡(Dead)这五种状态。
线程的生命周期图:
当线程启动后,它不可能一直“霸占”着CPU独自运行,所有CPU需要多条线程之间切换,于是线程状态也会多次运行,阻塞之间切换.
解析:
新建状态:当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值
就绪状态:当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行
运行状态:如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态
阻塞状态:当处于运行状态的线程失去所占用资源之后,便进入阻塞状态
死亡状态:线程在run()方法执行结束后进入死亡状态。此外,如果线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。
Synchronized关键字
(一)当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
(二)然而,当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object中的非synchronized同步代码块。
(三)当一个线程访问object的一个synchronized同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
(四)那也就是说,当一个线程访问object的一个synchronized同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
synchronized的好处是:
(1)确保线程互斥的访问同步代码
(2)保证共享变量的修改能够及时可见
(3)有效解决重排序问题。
什么是线程的控制呢?
(1)多线程中,当CPU在线程间切换时,需要保存线程的运行状态,再切回来时可以在之前的状态下继续执行;因此需要对线程进行良好的控制。
(2)控制线程的方式包含:判断线程是否处于活动状态,改变线程的优先级去优先执行某个线程,可中断当前线程执行其他线程等等
(3)在对线程进行控制前,我们需要先判断一下线程是否处于活动状态
活动状态就是线程启动并且尚未终止;当线程处于就绪状态或者运行状态,我们就认为线程是“存活”的
(4)进程中至少有一个线程,在众多线程中,有时需要优先执行一个线程,这个线程的优先级越高,得到CPU执行的机会就越大,反之就越小。 eg:对象名.setPriority()
线程的优先级使用1-10之间的整数来表示,数字越大,优先级越高;除了通过整数表示优先级,还可以通过Thread类的3个静态常量表示:MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY
如何设置线程的优先级?
设置线程优先级,只是增加CPU调度的机会;往往还需要直接设置线程中断,让CPU执行其他线程:
(1)可以使用静态方法sleep()方法来使正在执行的线程以指定的毫秒数暂停,进入睡眠状态,在该线程休眠时间内,CPU会调度其他线程。
(2)为了更好的操作线程,Thread类还提供了yield()方法,来暂停当前正在执行的线程对象,把机会让给相同或优先级更高的线程,因此, yield()方法也称为线程让步。
*(yield()会使线程进入就绪状态,不会进入阻塞状态)*
(3) 在多线程操作时,有时候还需要让某个线程插队,Thread类中提供了join()方法,来实现这一功能。当线程****调用join()方法****时,线程会进入阻塞状态,直到join方法加入的线程执行完毕,该线程才会继续执行。
(4)线程的run()方法完成。
下面有两个简单的测试题,我们一起来开动大脑看一下吧:
1.定义一个类,继承自Thread类,实现多线程;在定义一个类,实现Runnable接口,实现多线程;最后在测试类中调用两种方法实现的多线程,并设置线程的优先级。
package ZuoYe;
//继承Thread类的子线程
public class Methread extends Thread {
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println(this.getName()+"Methread run:"+i);
}
}
}
package ZuoYe;
//这是实现Runnable接口的子线程
public class Merunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"--->Merunnable run:"+i);
}
}
}
package ZuoYe;
public class Test { //测试类
public static void main(String[] args) {
Methread thread=new Methread();
thread.setName("这是我们第一个子线程"); //起名
thread.start(); //调用第一个子线程
//创建Runnable实现类的对象
Merunnable runnable=new Merunnable();
//以此对象作为参数构造Thread类
Thread meThread2=new Thread(runnable);
meThread2.setName("我们的第二个子线程");
//开启线程(子线程)
meThread2.start();
//设置优先级
meThread2.setPriority(1); //在1-10之间数值越大 执行的机会越大
thread.setPriority(5);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2:写两个线程,一个线程打印1-52的数字。另一个线程打印字母A-Z,打印两个数字,打印一个字母,按照顺序依次打印,打印结果类似于 12A、34B、56C等
package ZuoYe;
//一个线程打印1-52的数字
public class Five1 extends Thread{
private Object o;
public Five1(Object o) { //构造一个有参构造方法
this.o = o;
}
@Override
public void run() {
synchronized(o){
for(int i=1;i<=52;i++){
System.out.print(i);
if(i%2==0){
o.notifyAll(); //先唤醒所有的线程
try {
o.wait(); //等待
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println();
}
}
}
}
}
package ZuoYe;
//另一个线程打印字母A-Z,
public class Five2 extends Thread{
private Object o;
public Five2(Object o) { //构造一个有参构造方法
this.o = o;
}
public void run() {
synchronized(o){
for(char i='A';i<='Z';i++){
System.out.print(i);
o.notifyAll();
if(i!='Z'){ //最后一个不能进入等待状态
try {
o.wait();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
}
package ZuoYe;
public class Test01 {
public static void main(String[] args) throws InterruptedException {
final Object o=new Object(); //锁
Five1 f1=new Five1(o); //创建对象 参数是锁
Five2 f2=new Five2(o);
f1.start(); //开启线程
f2.start();
}
}
————————————————
版权声明:本文为CSDN博主「雪蛋不是蛋(^_−)☆」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_46291038/article/details/118400204