Java之多线程(常用方法、同步的简单介绍)

线程的常用方法

  • 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++语言实现 !
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值