java进程线程_Java进程线程笔记

什么是并行和并发?

并发和并行是即相似又有区别:(微观)

并行:指两个或多个事件在同一时刻发生; 强调的是时间点.

并发:指两个或多个事件在同一时间段内发生; 强调的是时间段.

进程和线程的区别?

进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响的,又称为轻型进程或进程元。 因为一个进程中的多个线程是并发运行的,那么从微观角度上考虑也是有先后顺序的,那么哪个线程执行完全取决于CPU调度器,程序员是控制不了的。我们可以把多线程并发性看作是多个线程在瞬间抢CPU资源,谁抢到资源谁就运行,这也造就了多线程的随机性。

怎么创建进程?

创建进程的方式有两种,我以windows上的记事本为例:

packagecom.StadyJava.day14;importjava.io.IOException;public classProcessDemo {public static void main(String[] args) throwsException {//方法1:使用Runtime

Runtime runtime=Runtime.getRuntime();

runtime.exec("notepad");//方法2:ProcessBuild

ProcessBuilder processBuilder=new ProcessBuilder("notepad");

processBuilder.start();

}

}

运行代码,此时会生成两个记事本。

我也尝试过启动其他的程序,但是计算器不认识,只有notepad这种计算机自带的才认识。

如果想要启动其他的程序,只能写上绝对路径

ProcessBuilder processBuilder=new ProcessBuilder("E:\\shuyunquan\\TIM\\Bin\\TIM.exe");

processBuilder.start();

这样我测试了,是可以的,但是没意思,这样你写了程序发布了也搞怪不了,别人电脑上的路径和你不一样。。。

接下来讲解线程。

下图是线程的一些常用的方法

33fd5e1764da3493f58dbe5a3f4d0280.png

创建进程的两个方法已经知道了,接下来看看创建线程的两个方法

创建线程方法1:使用继承Thread类

packagecom.StadyJava.day14Thread;importjava.lang.Thread;class Music extendsThread{public voidrun() {for (int i = 0; i < 50; i++) {

System.out.println("听音乐"+i);

}

}

}public classMusicThread {public static voidmain(String[] args) {for (int i = 0; i < 50; i++) {

System.out.println("打游戏"+i);if (i == 10) {

Music music=newMusic();

music.start();

}

}

}

}

注意,继承了Thread类之后要重写run方法,而且在调用的时候,只能使用start方法,不能调用run方法,切记!

输出的结果是在打游戏10之后,下面的打游戏和听音乐都是随机出现,因为主线程main和线程music在抢占资源,谁抢到谁执行,所以输出的结果是随机的

(注意!我使用Idea输出的结果是顺序的,不是随机的。我同学和我一样的代码使用Eclipse结果是随机的,我自己复制到Eclipse之后偶尔随机,Idea是死都不随机,这个我解决不了,希望以后知道原因或者有人告诉我)

创建线程的方法2:实现Runnable接口

其实Thread类也是实现了Runnable接口的,所以这个方法我感觉是本源??

packagecom.StadyJava.day14Thread;importjava.lang.Runnable;class MusicRun implementsRunnable{public voidrun() {for (int i = 0; i < 50; i++) {

System.out.println("听音乐"+i);

}

}

}public classMusicRunnable {public static voidmain(String[] args) {for (int i = 0; i < 50; i++) {

System.out.println("打游戏"+i);if (i == 10) {

Runnable music=newMusicRun();

Thread thread=newThread(music);

thread.start();

}

}

}

}

没什么特别的,就是生成一个runnable的对象,然后Thread对象加载进这个Runnable对象,最后start方法调用一下就完事了。这个结果我的Idea依然是显示的不符合我的期望的。

上面两种创建线程的方式是最常用的方式,一般也就足够了,下面介绍一下不怎么常用的

创建线程的方法3:匿名内部类创建线程

匿名内部类的格式:new 接口(){}  应该是这样的,待补充

其实匿名内部类创建线程还是使用上面的两种方式,只不过那个Music类我不需要去定义了,这就是匿名内部类,看代码吧

packagecom.StadyJava.day14Thread;importjava.lang.Runnable;class MusicRun implementsRunnable{public voidrun() {for (int i = 0; i < 50; i++) {

System.out.println("听音乐"+i);

}

}

}public classMusicRunnable {public static voidmain(String[] args) {for (int i = 0; i < 50; i++) {

System.out.println("打游戏"+i);if (i == 10) {//匿名内部类的形式1,使用接口

new Thread(newRunnable() {public voidrun() {for (int i = 0; i < 50; i++) {

System.out.println("听音乐"+i);

}

}

}).start();//匿名内部类的形式2,使用继承类

newThread(){public voidrun() {for (int i = 0; i < 50; i++) {

System.out.println("听音乐"+i);

}

}

}.start();

}

}

}

}

这回,输出的结果总算是符合我的预期了,可能是线程变成了3个,抢占资源激烈了些。。。

学了两种常见的创建线程的方法之后,他们之间有什么区别呢?

Thread创建线程和Runnable创建线程的区别

我写个例子,吃苹果大赛,3个人参加比赛,先使用继承Thread类创建线程的方式,代码如下:

packagecom.StadyJava.day14;class Person extendsjava.lang.Thread{private int num=50;publicPerson(String name){super(name);

}public voidrun() {for (int i = 0; i < 50; i++) {if (num >0) {

System.out.println(super.getName()+"吃了编号为"+num--+"的苹果");

}

}

}

}public classEatAppleThread {public static voidmain(String[] args) {//创建3个人,去参加吃苹果大赛

new Person("许嵩").start();new Person("林俊杰").start();new Person("蜀云泉").start();

}

}

这个输出的结果,就是许嵩,林俊杰,蜀云泉每个人都吃了50 个🍎。原因是因为我new了三个对象,没个对象都有num变量,他们之间互不干扰,如下图所示:

b3eb0d1d82192eb4e080c7906d72c40f.png

这样很不好,我的吃苹果大赛是总共50个🍎,你们3个人来吃就完事了。我们看看实现Runnable接口创建线程的方式是怎么样的,代码如下:

packagecom.StadyJava.day14;class Apple implementsRunnable{private int num=50;public voidrun() {for (int i = 0; i < 50; i++) {if (num >0) {//返回当前线程的引用Thread.currentThread(),再获取名字

System.out.println(Thread.currentThread().getName()+"吃了编号为"+num--+"的苹果");

}

}

}

}public classEatAppleRunnable {public static voidmain(String[] args) {//创建3个人,去参加吃苹果大赛

Apple apple=newApple();new Thread(apple,"许嵩").start();new Thread(apple,"林俊杰").start();new Thread(apple,"蜀云泉").start();

}

}

这次因为我传入的都是apple这个对象,这一个对象有50个🍎,创建了3个线程,这次输出的结果是OK的,原因如下图所示,3个线程共用了一个苹果对象。

250599d8c702efea75776aaeeff31633.png

从这个吃苹果比赛的例子中可以总结一下继承Thread类创建线程的方式和实现Runnable接口创建线程的方式的区别:

继承Thread类创建线程方式:

Java中类是单继承的,如果使用了继承Thread类创建线程,那么就 不能再有其他父类了,这是一个限制

从操作上来说,继承Thread类的方式更简单,获取线程名称也简单,直接getName就好了。操作简单,这是优点

从多线程共享资源的方面分析,继承方式不行,直接3个人,每个人50个苹果,没有实现共享,这是缺点

实现Runnable接口创建线程方式:

Java中类是可以实现多接口的,所以实现Runnable接口创建线程,也可以继续的去实现其他接口,也可以去继承类,设计优雅,这是优点

从操作上分析,实现接口方式有点复杂,获取线程名称的时候,必须使用Thread.currentThread()来获取当前线程的引用

从多线程共享资源的方面上,实现接口方式可以做到共享资源,3个人去吃50个苹果,共享资源。这是优点

综合上面的区别对比,我们的这个比赛。看来只能使用实现Runnable接口创建线程的方式来实现了。推荐以后创建线程,都使用实现Runnable接口的方式。

线程安全问题

拿上面写的实现接口创建线程的吃苹果比赛为例,这个是存在线程安全的,我们可以写一个线程休眠来看看,这样更容易观察。

需要说明,Thread.sleep线程休眠,需要使用try catch来扑捉异常,不能使用throw抛出,因为Runnable接口里面的run方法本身就没有throw的写法

packagecom.day14;class Apple implementsRunnable{private int num=50;public voidrun() {for (int i = 0; i < 50; i++) {if (num >0) {//线程休眠,必须使用try catch扑捉异常

try{

Thread.sleep(1000);

}catch(InterruptedException e) {

e.printStackTrace();

}//返回当前线程的引用Thread.currentThread(),再获取名字

System.out.println(Thread.currentThread().getName()+"吃了编号为"+num--+"的苹果");

}

}

}

}public classEatAppleRunnable {public static voidmain(String[] args) {//创建3个人,去参加吃苹果大赛

Apple apple=newApple();new Thread(apple,"许嵩").start();new Thread(apple,"林俊杰").start();new Thread(apple,"蜀云泉").start();

}

}

我就加了一个线程休眠,我们现在来看看输出的结果是什么样的

b1f4277edcaa4c6dcee7cd3c8d6f78bb.png

居然有0,还有-1 不是已经写了if(num>0)吗?出现这种情况的原因是,许嵩,林俊杰,蜀云泉这三个线程都在num>0的时候,例如num=1的时候,他们仨都拿到了资源,都可以去执行run方法

这个时候就会出现这种情况。通俗一点讲许嵩拿到了最后一个苹果,但是没吃,林俊杰和蜀云泉来抢。由于这个苹果不具备独占性,所以最后一个苹果被许嵩,林俊杰,蜀云泉都咬了一口。他们都宣称自己吃了苹果,所以就会出现0和-1的情况。这样显然是不允许的。这就是

多线程并发的访问一个资源产生的安全问题

要想解决这个问题,就必须要保证苹果的数量减少必须保证同步,许嵩拿了最后一个苹果,这个时候苹果数量同步为0,剩下的人不能再抢了。

许嵩这个线程在操作的时候,林俊杰和蜀云泉只能等着。等许嵩操作完了。许嵩,林俊杰和蜀云泉才有机会去重新抢资源。

意思是这样,方法有3种

方法1.同步代码块

方法2.同步方法

方法3.锁机制(Lock)

方法1:同步代码块

语法:

synchronized(同步锁){

需要同步操作的代码

}

同步锁:为了保证每个线程都能单独的执行操作,java线程同步的机制。同步锁也叫

同步监听对象/同步锁/同步监听器/互斥锁

这些都是别名,就像茴香豆的“茴”字有几种写法一样,都是别名。

Java程序中的任何对象都可以作为同步监听对象,但是我们一般把多个线程同时访问的共享资源作为同步监听对象。监听其它的单独的对象有啥意义。注意,在任何时候,最多运行一个线程拥有同步锁。

代码如下:

packagecom.day14;class Apple implementsRunnable{private int num=50;public voidrun() {for (int i = 0; i < 50; i++) {//方法1:同步代码块//由于我多个线程共享的是Apple对象,所以同步锁就是this,当前类的对象。不能使用num变量,因为num变量一直在变化

synchronized (this) {if (num > 0) {try{

Thread.sleep(1000);

}catch(InterruptedException e) {

e.printStackTrace();

}//返回当前线程的引用Thread.currentThread(),再获取名字

System.out.println(Thread.currentThread().getName() + "吃了编号为" + num-- + "的苹果");

}

}

}

}

}public classEatAppleRunnable {public static voidmain(String[] args) {//创建3个人,去参加吃苹果大赛

Apple apple = newApple();new Thread(apple, "许嵩").start();new Thread(apple, "林俊杰").start();new Thread(apple, "蜀云泉").start();

}

}

运行结果如下:

d7b3d64a85d1126734fd4eb9976669d7.png

这下不会出现抢苹果事件了,也不会出现数量为0和-1的情况了。

方法2:同步方法

synchronize修饰的方法就是同步方法,保证当前线程执行的时候,其它线程只能等待

语法:

synchronize public void Dowork(){

执行操作

}

同步锁:对于非static方法,同步锁就是this,对于静态方法,同步锁就是当前方法所在类的字节码对象(类.class)

注意!不能使用synchronize修饰run方法。

packagecom.day14;class Apple implementsRunnable{private int num=50;public voidrun() {for (int i = 0; i < 50; i++) {

eat();

}

}//方法2:同步方法,直接用synchronized修饰方法

synchronized private voideat(){if (num > 0) {try{

Thread.sleep(10);

}catch(InterruptedException e) {

e.printStackTrace();

}//返回当前线程的引用Thread.currentThread(),再获取名字

System.out.println(Thread.currentThread().getName() + "吃了编号为" + num-- + "的苹果");

}

}

}public classEatAppleRunnable {public static voidmain(String[] args) {//创建3个人,去参加吃苹果大赛

Apple apple = newApple();new Thread(apple, "许嵩").start();new Thread(apple, "林俊杰").start();new Thread(apple, "蜀云泉").start();

}

}

运行结果也是OK的

15a3bd851df8f2ae0dbb5f9b47b7b0cd.png

或许有人会想,既然方法加了个synchronize就线程安全了,那我把所有的方法都加上synchronize不就得了。答案是不行滴

synchronize的优缺点:

优点:保证了多线程并发访问时的同步操作,避免了多线程操作的安全问题。

缺点:使用synchronize同步方法/同步代码块会导致性能降低。

方法3:锁机制(Lock)

锁机制用到的是Lock这个接口,当然我们在写代码的时候使用的是Lock接口的一个实现子类,叫ReentrantLock

语法:

private final Lock lock=newReentrantLock();try{

线程操作代码

}catch(InterruptedException e) {

e.printStackTrace();

}finally{

lock.unlock();

}

还是上面的吃苹果比赛,使用锁机制的代码如下:

packagecom.day14;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;class Apple implementsRunnable{//创建一个Lock接口的实现子类对象。ReentrantLock是Lock接口的一个子类的实现。

private final Lock lock=newReentrantLock();private int num=50;public voidrun() {for (int i = 0; i < 50; i++) {

eat();

}

}//方法3:同步锁(Lock)的方式,这个方式和方法2的同步方式很类似。

private voideat(){//进入方法首先上锁

lock.lock();if (num > 0) {try{//返回当前线程的引用Thread.currentThread(),再获取名字

System.out.println(Thread.currentThread().getName() + "吃了编号为" + num-- + "的苹果");

Thread.sleep(10);

}catch(InterruptedException e) {

e.printStackTrace();

}finally{//结束后,记得释放锁

lock.unlock();

}

}

}

}public classEatAppleRunnable {public static voidmain(String[] args) {//创建3个人,去参加吃苹果大赛

Apple apple = newApple();new Thread(apple, "许嵩").start();new Thread(apple, "林俊杰").start();new Thread(apple, "蜀云泉").start();

}

}

这个锁机制是不是和synchronize同步方法很相像,只不过同步方法是synchronize修饰的方法,而锁机制是在方法里面上锁,释放锁。

锁机制和同步代码块/同步方法比,范围更广泛。也就是说锁机制包括了同步代码块和方法,而且范围更大,更加面向对象。上锁,释放锁都自己来写,还有创建实现Lock接口的对象。

用线程实现生产者消费者问题

现在来讲一个生产者和消费者的问题,讲定生产者生产一些东西,放到分享池中,然后消费者去分享池中消费东西,大概就是下图那样的展示:

6de965566bb8f123b32d5b75129861c4.png

这就是我们的生产者和消费者的模型,我们要根据这个写代码。记得加上上面学习的同步锁知识。

分享池代码:

packagecom.day15;public classShareResource {privateString name;privateString sex;private intnum;private boolean isEmpty=true;synchronized public void push(String name,String sex,intnum) {try{while (!isEmpty) {//如果不为空的时候,生产者线程就等待

this.wait();

}//开始生产,生产过后,要isEmpty变成为空,然后唤醒其它的线程

this.name=name;this.sex=sex;this.num=num;

isEmpty=false;this.notifyAll();

}catch(InterruptedException e) {

e.printStackTrace();

}

}synchronized public voidpopup(){try{while(isEmpty) {this.wait();

}

System.out.println(this.name + this.sex + this.num);

isEmpty=true;this.notifyAll();

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

生产者代码:

packagecom.day15;//生产者线程

public class Producer implementsRunnable{//分享池对象

public ShareResource shareResource=null;publicProducer(ShareResource shareResource) {this.shareResource=shareResource;

}

@Overridesynchronized public voidrun() {for (int i = 0; i <50 ; i++) {if (i % 2==0) {

shareResource.push("许嵩","男",i);

}else{

shareResource.push("梦中人","女",i);

}

}

}

}

消费者代码:

packagecom.day15;public class Consumer implementsRunnable {//分享池对象

public ShareResource shareResource=null;publicConsumer(ShareResource shareResource) {this.shareResource=shareResource;

}

@Overridesynchronized public voidrun() {for (int i = 0; i <50 ; i++) {

shareResource.popup();

}

}

}

执行测试代码:

packagecom.day15;//测试类

public classRunTest {public static voidmain(String[] args) {//创建生产者和消费者共同的资源对象

ShareResource resource=newShareResource();//启动生产者线程

new Thread(newProducer(resource)).start();new Thread(newProducer(resource)).start();//启动消费者线程

new Thread(newConsumer(resource)).start();new Thread(newConsumer(resource)).start();

}

}

代码就是这些,我实行的是生产者生产一个东西,消费者就去消费,我这里是直接打印出来了。生产者生产之后就去wait休息,等到东西没了才开始干活。这里我们学到了两个新的方法

1.wait()方法:线程休眠,除了被唤醒,否则就会一直睡觉休息,和睡美人是差不多了,没有人唤醒是不会苏醒的。wait方法里面可以加参数,毫秒,就是没人唤醒的话就自己醒(好惨啊...)

2.notifyAll()方法:唤醒除自己以外所有的线程,还有一个方法是notify(),唤醒随机的一个线程

最后我们看看输出的结果:

8db03c55fd117a9ebf82c63f7d70273b.png

通过上面同步方法synchronized和线程的wait方法,notify方法很好的完成了生产者和消费者的问题。这里需要说明的是,wait方法和notify方法都必须需要同步锁,那么,我现在想用Lock锁机制去完成生产者消费者问题,那该怎么办呢?

锁机制Lock完成生产者和消费者问题

锁机制Lock是无法使用wait和notify方法的,那使用锁机制的线程之间怎么进行通信呢?

从Java5开始就为锁机制的线程提供了Condition接口,用于线程直接的通信,主要使用的方法有:

1.await()方法,相当于wait()方法,线程睡眠

2.signal()方法,相当于notify()方法,随机的唤醒任意一个线程

3.signalAll()方法,相当于notifyAll()方法,唤醒除了自己以外的所有的线程

然后代码其实就修改了分享池的代码,放出来看一下:

packagecom.StadyJava.day15;importjava.util.concurrent.locks.Condition;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;public classShareResource {privateString name;privateString sex;private intnum;private boolean isEmpty=true;private final Lock lock=newReentrantLock();//创建lock锁机制的Condition对象

private Condition condition=lock.newCondition();public void push(String name,String sex,intnum) {

lock.lock();try{while (!isEmpty) {

condition.await();

}//开始生产,生产过后,要isEmpty变成为空,然后唤醒其它的线程

this.name=name;this.sex=sex;this.num=num;

isEmpty=false;

condition.signalAll();

}catch(Exception e) {

e.printStackTrace();

}finally{

lock.unlock();

}

}synchronized public voidpopup(){

lock.lock();try{while(isEmpty) {

condition.await();

}

System.out.println(this.name + this.sex + this.num);

isEmpty=true;

condition.signalAll();

}catch(Exception e) {

e.printStackTrace();

}finally{

lock.unlock();

}

}

}

其实没什么大变化,就是方法换了名字而已。

线程的死锁

死锁就是两个线程互相在等待对方释放锁,死锁的现象一旦出现,是解决不了的,所以死锁现象只能避免,不能解决。

关于死锁,有一个超级经典的例子,就是哲学家就餐问题

ed058611bb3ab2c5b6cb196d4b830a11.png

线程的6种状态

如下图所示,Java线程有6种状态,现在来介绍一下各种状态。

03048199a88569f87ecf18729eed555f.png

1edf52b6817131c72cc5abc960276942.png

a8f5bc214f5c5f866c9508ec3205f122.png

线程的核心内库(几个重要的方法)

线程有几个很重要的方法需要讲一下

1.线程睡眠,sleep方法

这个方法是不是和上面讲的wait方法很像?其实不一样,wait睡觉之后,同步锁就释放了。sleep睡觉的时候,同步锁是紧紧的抓住不松手的

这个方法大部分用来模拟网络延迟,因为你刷新网页的时候不是会转圈圈吗,可以模拟这个。代码中也有很多地方用这个来写东西,例如我想模拟一个定时炸弹,我可以这样写代码:

packagecom.StadyJava.day15;public classThreadDemo {public static voidmain(String[] args) {for (int i = 10; i > 0; i--) {

System.out.println("离爆炸还有"+i+"秒");

}

System.out.println("嘣,爆炸啦");

}

}

这样写,没问题吧,有问题的,我想定时,但是这个一运行,结果全出来了,不是想要的结果,所以我们可以加一个睡眠1秒来实现,代码如下:

packagecom.StadyJava.day15;public classThreadDemo {public static void main(String[] args) throwsException {for (int i = 10; i > 0; i--) {

System.out.println("离爆炸还有"+i+"秒");

Thread.sleep(1000);

}

System.out.println("嘣,爆炸啦");

}

}

记得,sleep方法是要抛出异常的

这样就可以了,实现了倒计时的效果。还有几个例子,例如坦克发射子弹,那这个子弹肯定是不断位移的,我们设置好之后,子弹可能瞬间就打出去了,你根本看不到子弹的运行轨迹。为了仔细的观察,或者实现子弹慢速射击的一个要求,我们也可以去sleep一下。还有NBA投篮游戏也是,都可以去试试。

2.联合线程,join方法

线程的join方法表示一个线程等待另一个线程完成后才执行。就是说把当前线程和当前线程所在的线程联合成一个线程。join方法被调用之后,线程对象处于阻塞状态。

适用于A线程需要等到B线程执行完毕,再拿B线程的结果再继续运行A线程。写个代码

packagecom.StadyJava.day15;class Join extendsThread{public voidrun() {for (int i = 0; i < 50; i++) {

System.out.println("join"+i);

}

}

}public classThreadDemo {public static void main(String[] args) throwsException {

Join joinThread=newJoin();for (int i = 0; i < 50; i++) {

System.out.println("main"+i);if (i == 1) {

joinThread.start();

}else if (i == 20) {

joinThread.join();

}

}

}

}

运行结果如下:

ec8dc9c5ee47935e1cd421b51a5763f0.png

a0f469fbc1c56aa021893a3723c8a378.png

Idea副线程想和主线程抢资源真难。。。。运行了好几次才抢到。。。我们可以看到在主线程等于1的时候,两个线程开始抢占资源打印输出。在主线程为20的时候,joinThread线程就调用了join方法,这个时候他们俩就变成了联合线程,主线程main开始进入阻塞状态,必须等到joinThread线程执行完毕才可以执行。

3.后台线程

在后台运行,其目的是为其他线程提供服务,也称为“守护线程。

JVM的垃圾回收器就是典型的后台线程。

特点:若所有的前台线程都死亡,后台线程自动死亡。

测试线程对象是否为后台线程:使用thread.isDaemon()。

前台线程创建的线程默认是前台线程,并且当且仅当创建线程是后台线程时,新线程才是后台线程。

设置后台线程:thread.setDaemon(true),该方法必须在start方法调用前,否则出现IllegalThreadStateException异常。

packagecom.StadyJava.day15;class Daemon extendsThread{public voidrun() {for (int i = 0; i < 50; i++) {

System.out.println(super.getName()+"-"+super.isDaemon()+i);

}

}

}public classThreadDemo {public static void main(String[] args) throwsException {for (int i = 0; i < 50; i++) {

System.out.println("main"+i);if (i == 1) {

Daemon daemon=newDaemon();

daemon.setDaemon(true);//设置线程为后台线程,必须先设置后台线程才能start开启,先start开启再设置会报错

daemon.start();

Thread.sleep(10);

}

}

}

}

就一个判断是否是后台线程的方法 isDaemon() 和一个设置线程为后台线程的方法 setDaemon(true)

如果没有了前台线程,后台线程会死亡。

4.线程的优先级

优先级有两个方法

1.setPriority() 获取当前线程的优先级,main线程的优先级是5,默认的都是5

2.setPriority() 设置线程的优先级,不同的操作系统是不一样的,Linux和Windows都不一样。但是有3个数字是统一的,分别是1,5,10  1是最低,10是最高。所以用这3个数字就可以了。

注意:并不是优先级高的线程一定先执行,而是说这个线程有更多的机会去执行。执行的几率大了一些。

下面看一个代码

packagecom.StadyJava.day15;class PriorityThread extendsThread{publicPriorityThread (String name){super(name);

}public voidrun() {for (int i = 0; i < 50; i++) {

System.out.println("我是"+getName()+i);

}

}

}public classPriority {public static voidmain(String[] args) {

PriorityThread priorityThread1=new PriorityThread("优先级高的线程");

PriorityThread priorityThread2=new PriorityThread("优先级低的线程");//priorityThread1.setPriority(Thread.MIN_PRIORITY);也可以使用这个,但是数字更简单

priorityThread1.setPriority(10);

priorityThread2.setPriority(5);

priorityThread2.start();

priorityThread1.start();

}

}

9591c2335c933fb7db519a559b14b336.png

看结果也知道,优先级高并不是一定先执行。

5.线程的礼让

古有孔融让梨,那么线程也有一个礼让的方法叫 yield方法。这个方法比较特别,他把自己执行的机会让给那些优先级高的线程,提出这个请求给调度器CPU,但是CPU可以同意这个请求也可以无视这个请求

这个yield方法和sleep方法的区别如下:

1.都可以使得当前处于运行状态的线程放弃执行的机会,让给其它线程

2.sleep方法会让给其它线程,随机的让。yield方法会让给那些优先级高的线程。

3.调用sleep方法后,线程会进入计时等待状态。调用yield方法后,线程会进入就绪状态。

这个yield方法一般是不用的。不使用。。。。。在调试和测试线程的时候,可能会重现多线程的错误,可能。。。。所以还是了解一下就好吧

6.线程的定时器和线程组

定时器,就是定时去执行啦,直接看代码

packagecom.StadyJava.day15;importjava.util.Timer;importjava.util.TimerTask;class Vae extendsTimerTask {public voidrun() {

System.out.println("大家好,我是Vae");

}

}public classTimerDemo {public static voidmain(String[] args) {new Timer().schedule(new Vae(),3000,1000);

}

}

注意,我的Vae类是继承的TimeTask类,不是Thread类。定时器的方法就是schedule方法,第一个参数就是TimeTask对象,第二个参数就是第几秒出现执行,第三个参数就是间隔多少秒执行一次。

线程组,就是多个相同的线程在一个组里面,就像老师讲课,给A同学讲一遍,再给B同学讲一遍,再给C同学讲一遍。。。。这样太麻烦。直接让ABC三个同学都过来,一起听就完事了。这就是线程组的意义

线程组的特点:

1.如果线程A创建了线程B,那么B和A一定是一组的。

2.一个线程一旦加入了线程组,一辈子就是这个组的,一天是不良人,一辈子都是不良人。

当Java程序运行时,JVM会创建一个main线程组,默认所有的线程都是main线程组的

线程的知识差不多就这些了,下面来问几个问题,看看都能不能回答

1.synchronized和Lock的区别是什么?

synchronized是一个修饰符,Lock是一个类。这是最本质的区别。

2.为什么wait方法和notify方法/notifyAll方法不在Thread类中,而在Object类中?

简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中,因为锁属于对象。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值