void transferMoney(User from, User to, float amount){
to.setMoney(to.getBalance() + amount);
from.setMoney(from.getBalance() - amount);
}
线程的生命周期为:新建、就绪、运行、阻塞、死亡等五种状态。
这里写图片描述
使用new关键字和实现Thread类及其子类来新建一个线程对象后,该对象就处于 新建状态 。它能保持这个状态直到start()
方法开启这个线程。
注意:当一个线程处于新建状态时,它仅仅是一个空的线程对象,系统不会为它分配资源。
当线程对象调用start()
方法之后,该线程就进入 就绪状态 。就绪状态的线程处于就绪队列当中,等待JVM里线程调度器的调度。
Thread.strat();//调用start()方法
如果就绪状态的线程的线程获取CPU资源,就可以开始执行run()
方法,此时线程就处于运行状态。此处的线程最为复杂,它可以变为阻塞状态、就绪状态、死亡状态。
如果一个线程执行了sleep(睡眠)
、suspend(挂起)
等方法,失去所占用资源之后,该线程就从运行进入阻塞状态。在睡眠时间已到或者获得设备资源后可以进入就绪状态,可以分为三种:
wait()
方法,使线程进入到等待阻塞状态。synchronized
同步锁失败(因为同步锁被其他线程占用)。sleep()
或join()
发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep()
状态超时,join()
等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程控制分为:线程睡眠、线程加入、线程让步、后台线程 。
Thread.sleep(millis);//静态方法,使用类名调用。使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行。当中的millis是表示毫秒
thread.join();//让当前线程等待thread线程结束之后才能继续运行
thread.join(millis);//让当前线程等待thead线程millis毫秒后运行
thread.yield();//该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只是让同优先级的线程有执行的机会。
thread.setDaemon(boolean on);//设置后台线程
后台线程会随着主程序的结束而结束,但是前台进程则不会;或者说只要一个前台线程未退出,进程就不会终止。
默认情况下,程序员创建的线程的是用户线程:用setDaemon(ture)可以设置线程为后台程序;而用isDemon()则可以判断一个线程是前台线程还是后台线程;
注意:setDemon方法必须在start方法前面设置,否则程序会抛出IllgalThreadStateException异常。
在Java中,每一个线程都有一个优先级。默认情况,一个线程将继承其父线程的优先级。线程用数字来表示,范围从1到10,一个线程的缺省优先级是5。我们可以通过线程的方法进行设置线程的优先级。
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
int getPriority();//获取线程优先级
void setPriority(int newPriority);//设置线程对象的优先级
注意: 可以通过setPriority方法(final的,不能被子类重载)更改优先级。优先级不能超出1-10的取值范围,否则抛出IllageArgumentException异常。
注意:不要假定高优先级线程一定先于低优先级的线程执行,不要有逻辑依赖于线程优先级,否则可能产生意外结果。
有些程序包含了相当数量的线程。这时如果按照线程的功能将他们分成不同的类别将很有用。
线程组可以用来同时对一组线程进行操作。
hreadGroup group = new ThreadGroup(groupName);
将某个线程添加给某个线程组,使用如下方法:
Thread thread = new Thread(group, threadName);
要中断线程组中所有的线程运行,使用下面的方法:
group. interrupt();
要确定某个线程组中的任一线程是否仍处于可运行状态:
if (group. activeCount() == 0){}
线程组有一个很好的特性,那就是如果某个线程由于异常事件而成为死线程,你将会得到通知
为了确保在任何时刻一个共享对象只被一个线程使用,必须使用“线程同步(synchronized)”
第一种:使用同步方法
synchronized void methodA(){}
第二种:使用同步代码块
synchronized(obj){//obj是被锁定的对象
//这里写入需要同步的语句
}
用synchronized来标识的块或方法即为监视器监视的部分。只有使用synchronized ,才能利用对象的监视器功能。
调用任何synchronize方法时,对象会被锁定。不可以再调用那个对象的任何synchronize方法,除非第一个方法完成了自己的工作,并解除了锁定。因此,一个特定对象的所有synchronize方法都共享着一把锁,而且这把锁能锁定防止多个方法对通用内存的同时进行写操作(比如同时有多个线程
)。
每个类也有自己的一把锁(作为类的Class对象的一部分),所以synchronized static方法可在一个类的范围内被相互间锁定起来,防止与static数据的接触。 一般情况下,只在方法的层次上使用同步。
在多线程程序中,可能出现生产者-消费者问题,即等待同步数据的问题。
这里写图片描述
可能出现的问题是:
1.生产者比消费者快时,消费者会漏掉一些数据没有取到 。
2.消费者比生产者快时,消费者取相同的数据。
当中的notify()
和 wait()
用来协调读取关系,notify()
和wait ()
都只能从同步方法中调用。
1.如果两个或多个线程修改一个对象,请将执行修改的方法声明为synchronized方法。受到对象修改影响的只读方法也必须实现同步。
2.如果一个线程必须等待某个对象的状态出现变更,那么它应该在对象的内部等待而不是在外边等待。这可以通过进入一个synchronized方法,并且调用wait方法来实现。
3.不要在synchronized方法中花费大量的时间。大多数操作只是更新数据,然后很快返回。
4.每当一个方法改变某个对象的状态时,它就应该调用notifyAll方法。这样可以给等待线程一个机会,以便查看环境有没有发生变化
5.记住,wait和notifyAll/notify方法都属于Object类的方法,而不是Thread类的方法。反复检查你对wait方法的调用与同一对象上的通知是否匹配。
定义:当所有的线程都在等待得到某个资源后才能继续运行下去时,整个程序将被挂起,这种情况就叫做线程死锁。
有效利用多线程的关键是理解程序是并发执行而不是串行执行的。例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程。
通过对多线程的使用,可以编写出非常高效的程序。不过请注意,如果你创建太多的线程,程序执行的效率实际上是降低了,而不是提。。
上下文的切换开销也很重要,如果你创建了太多的线程,CPU 花费在上下文的切换的时间将多于执行程序的时!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。