线程的常用方法
-
public static Thread currentThread():表示当前正在运行的线程
-
getName():获取线程名称
-
setName(String name):设置当前线程名称
-
1.休眠
public static void sleep(long millis)
当前线程主动休眠millis毫秒.
- sleep是Thread类的静态方法,可以通过Thread类来直接调用
- 里面包含了两个重载的方法:
- sleep(millis) 一参代表毫秒
- sleep(millis, nanos) 两参毫秒和纳秒
注意该方法会抛出一个InterruptedException中断异常
//示例
public class TestSleep {
public static void main(String[] args) {
ThreadSleep mt = new ThreadSleep() ; // 实例化Runnable子类对象
Thread t = new Thread(mt,"线程"); // 实例化Thread对象
t.start() ; // 启动线程
}
}
class ThreadSleep implements Runnable{ // 实现Runnable接口
public void run(){ // 覆写run()方法
for(int i=0;i<100;i++){
try{
//每隔500毫秒执行一次
Thread.sleep(500) ; // 线程休眠
}catch(InterruptedException e){
}
// 取得当前线程的名字
System.out.println(Thread.currentThread().getName()+ "运行,i = " + i) ; // 取得当前线程的名字
}
}
}
执行结果
- 2.放弃(礼让)
public static void yield()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
- yield()方法意思是主动放弃cpu执行资源,让其他线程对象与自己再次进行cpu资源的抢占
- 这里可能会面临一个问题是,自己虽然把资源让出来,但是有可能当前线程对象又抢得了cpu资源,那么自己又再一次执行
//示例
public class TestYield extends Thread{
//重写run()方法
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
//当 i = 800 时,Thread进程放弃时间片
if(i == 800) {
yield();
}
}
}
//main方法进行测试
public static void main(String[] args) {
//实例化TestYield对象
TestYield ty = new TestYield();
ty.start();//启动线程
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
执行结果
- 3.结合(强制运行)
public final void join()
允许其他线程加入到当前线程中
- join()方法,可以使得要加入的线程一次性执行完成(线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。)
//示例
public class TestJoin extends Thread{
//重写run()方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
//main方法进行测试
public static void main(String[] args) throws InterruptedException {
//实例化TestJoin对象
TestJoin ty = new TestJoin();
ty.start();//启动线程
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
//当i = 50 时,调用join()方法
if(i == 50) {
ty.join();
}
}
}
}
执行结果
- 4.中断
public void interrupt()
当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。
//示例
public class TestInterruot {
public static void main(String args[]){
ThreadInterruot mt = new ThreadInterruot() ; // 实例化Runnable子类对象
Thread t = new Thread(mt,"线程"); // 实例化Thread对象
t.start() ; // 启动线程
try{
Thread.sleep(2000) ; // 线程休眠2秒
}catch(InterruptedException e){
System.out.println("3、休眠被终止") ;
}
t.interrupt() ; // 中断线程执行
}
}
class ThreadInterruot implements Runnable{ // 实现Runnable接口
public void run(){ // 覆写run()方法
System.out.println("1、进入run()方法") ;
try{
Thread.sleep(3000) ; // 线程休眠10秒
System.out.println("2、已经完成了休眠") ;
}catch(InterruptedException e){
System.out.println("3、休眠被终止") ;
return ; // 返回调用处
}
System.out.println("4、run()方法正常结束") ;
}
}
执行结果
- 5."守护"线程
public final void setDaemon(boolean on)
- 可以通过setDaemon(true)设置当前线程为一个“守护”线程
- "守护"线程的特点是:随着主线程的结束,守护线程可以不执行完而提前终止
- setDaemon(true)默认值为false,该方法也要在start()之前调用
public class TestsetDaemon extends Thread{
//重写run()方法
@Override
public void run() {
//定义的大一些
for (int i = 0; i < 2000; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
TestsetDaemon testsetDaemon = new TestsetDaemon();
//设置为守护线程
testsetDaemon.setDaemon(true);
testsetDaemon.start();
//设置的小一些,方便对比
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
执行结果
- 6.判断是否为"守护线程"
public final boolean isDaemon()
可以通过线程对象的isDaemon()方法来判断给定线程是否为一个“守护”线程
public class TestsetDaemon extends Thread{
//重写run()方法
@Override
public void run() {
//定义的大一些
for (int i = 0; i < 2000; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
TestsetDaemon testsetDaemon = new TestsetDaemon();
//设置为守护线程
testsetDaemon.setDaemon(true);
testsetDaemon.start();
//设置的小一些,方便对比
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
//判断换是否为"守护"线程
System.out.println(testsetDaemon.isDaemon());
}
}
执行结果
- 7.线程的优先级
public final void setPriority(int newPriority)
//常用的优先级常量
public final static int MIN_PRIORITY = 1;
//常用的优先级常量
public final static int NORM_PRIORITY = 5;
//常用的优先级常量
public final static int MAX_PRIORITY = 10;
- 在线程对象之上设置线程的优先级,优先级高的不见得一定会先执行完成,只是优先执行的概率更大一些
- 设置优先级有三个常量和10个值,如果是三个特殊值,建议使用常量,因为常量具备更好的可读性
- 注意设置优先级的值只能是1-10的范围,如果设置了其他值,会抛出IllegalArgumentException异常对象
- 设置线程的优先级一定要在线程的start()之前设置。
//示例
public class TestsetPriority extends Thread{
//重写run()方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
//创建4个对象
TestsetPriority ty1 = new TestsetPriority();
TestsetPriority ty2 = new TestsetPriority();
TestsetPriority ty3 = new TestsetPriority();
TestsetPriority ty4 = new TestsetPriority();
//赋予不同的优先级
ty1.setPriority(MAX_PRIORITY);
ty2.setPriority(NORM_PRIORITY);
ty3.setPriority(2);
ty4.setPriority(MIN_PRIORITY);
ty1.start();
ty2.start();
ty3.start();
ty4.start();
}
}
执行结果
注:在线程对象之上设置线程的优先级,优先级高的不见得一定会先执行完成,只是优先执行的概率更大一些
- 8等待
public final void wait()
public final void wait(long timeout)
-
必须在对obj加锁的同步代码块.在一个线程中,调用obj.wait()时,此线程会释放其拥有的所有锁标记.同时此线程阻塞在o的等待队列中.释放锁,进入等待对列
-
9.通知
public final void notify()
public final void notifyAll()
-
必须在对obj加锁的同步代码块中.从obj的Waiting中释放一个或全部线程,对自身没有影响
-
10.强制停止线程
@Deprecated
public final void stop() :已过时的方法,但是可以使用!强迫线程停止执行
@Deprecated:JDK提供内置注解:标记方法是否以过时!
同步
线程的安全问题
同步代码块
synchronized(临界资源对象){//为临界资源对象加锁
//原子操作
}
注
- 每个对象都有一个互斥锁标记,用来分配给线程的
- 只用拥有对象互斥锁标记的线程,才能进入该对象加锁的同步代码块
- 线程退出同步代码块时,会释放相应的互斥锁标记.
同步方法
synchronized 返回值类型 方法名称(形参列表){//对当前对象(this)加锁
//代码(原子操作)
}
注
- 只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中
- 线程退出同步方法是,会释放相应的互斥锁标记
同步规则
注意:
- 只有在调用包含同步代码块的方法时,或者同步方法时,才需要对象的锁标记
- 如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用
已知JDK中线程安全的类
- StringBuffer
- Vector
- Hashtable
- 以上类中的公开方法,均为synchonized修饰的同步方法
附加知识点
sleep()和wait()对比
共同点:
- 都会抛出InterruptedException中断异常
不同点
sleep - sleep()方法是Thread类的静态方法,是线程用来控制自身流程的,它会使此线程暂停执行一段时间,而把执行机会让给其他线程,等到计时时间一到,此线程会自动苏醒。
- 可以有一参、两参的重载方法
- sleep可以在任何地方使用(使用范围)
- 由于sleep()方法的主要作用是让线程暂停一段时间,时间一到则自动恢复,不涉及线程间的通信,因此调用sleep()方法并不会释放锁。
- sleep必须捕获异常,在sleep的过程中,有可能被其他对象调用它的interrupt(),产生InterruptedException异常。
wait
- 而wait()方法是Object类的方法,用于线程间的通信,这个方法会使当前拥有该对象锁的进程等待,直到其他线程用调用notify()或notifyAll()时才苏醒过来,开发人员也可以给它指定一个时间使其自动醒来。
- 可以有无参,一参和两参的重载方法
- wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用
- wait()方法则不同,当调用wait()方法后,线程会释放掉它所占用的锁,从而使线程所在对象中的其他synchronized数据可被别的线程使用。
- wait,notify和notifyAll不需要捕获异常
校验多线程是否是安全问题的标准是什么?如何解决?
标准
- 当前程序是否是多线程环境
- 是否拥有共享数据
- 是否有多条语句对共享数据进行操作
解决
- 多线程环境 ------>无法解决
- 对共享数据优化 ------>无法解决
- 多条语句对共享数据进行操作 ------>可以解决 ------> 加锁------>同步(同步代码块或者同步方法)
为什么wait, notify 和 notifyAll这些方法不在thread类里面?而是定义在Objec类中
- Java提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。
- 由于wait,notify,notifyAll都是锁级别的操作,所以把他们定义在object类中因为锁属于对象。
Java能够开启线程吗?
- Java不能够直接开启线程的!
- start方法—通过JVM调用 ,start方法本身就是同步方法----线程安全的方法
- 在start方法中调用了start0():private native void start0();
间接实现的,本地方法----C++语言实现 !