java入门学习(15)—多线程

一、什么是多线程?及其特点?如何执行多线程?

①一般情况下,只有一个顺序执行流的程序我们称其为多单线程,而多线程就是可以包括多个顺序执行流。

②多个执行流之间互不干扰;当一个程序进入内存运行时,即变成一个进程,进程是处于运行状态的程序,具有独立功能,是系统进行资源分配和调度的独立单位。其中线程被称为轻量级进程,线程是进程的组成部分,一个进程可以包括多个线程,线程可以拥有自己的堆栈,程序极计数器,和自己的局部变量,但是没有紫铜资源。

③它与父进程共享系统资源,其中主进程里的多个线程是利用抢占式的多任务策略,是随机执行的,线层是独立运行的,并不知道其他线程的存在。(一个运行的程序最低包含一个进程,一个进程里面可以包含多个线程,但是最起码包括一个线程。

二、并发和并行的区别?

并发(concurrency):多个线程快速轮换的执行,同时刻只有一个线程执行。

并行(Parallel:在有两个以上的CPU时,可以一起执行不同进程,(一起执行)。

三、多线程优点?

a、其在功能是类似于多进程,但是创建成本低,因为其功用系统资源。

b、创建成本低,而且java多线程性能优越。

c、各个线程共享内存,线程之间的通讯简单,方便。

四、如何创建多线程?

⑴利用继承Thread,重写run()方法的方式创建多线程。下面是示例代码:

<span style="font-size:18px;">//继承Thread类创建多线程
public class FirstThread extends Thread
{
			
<span style="white-space:pre">	</span>@Override<span style="font-family: Arial, Helvetica, sans-serif;">//该方法是重写父类的方法,因为父类里没有返回值,</span>
	public void run()<span style="font-family: Arial, Helvetica, sans-serif;">//所以重写也不能有返回值,不能声明超出异常</span>
	{
		for(int i=0;i<100;i++)
		{
			//获取当前的线程
			System.out.println(Thread.currentThread().getName()+"-----"+i);
		}
	}
	public static void main(String [] args)
	{
		new FirstThread().start();
				for(int i=0;i<100;i++)
		{
			//获取当前的线程
			System.out.println(Thread.currentThread().getName()+"-----"+i);
		}
	}</span>
}

    注:①run()方法下的是线程的执行体。

        启动线程用的是调用Thread的Start方法,(注意,如果调用run方法,只是执行run方法,而不是多线程。

        java默认有一个main主线程。

        ④使用Thread类的方法来创建的线程类,多条线程之间无法共享线程类的实例变量。

        ⑤可以通过其内的方法setName(String name) ,类为线程设置名字。

⑵实现Runnable类,重写一个run()方法的方式实现多线程的创建。其代码基本思路同上。但是其启动方式不一样,由于继承的类没有start()的线程启动方法,所以其启动方式较为特殊:

 ●启动方法:①获取Runnable类的线程实例:->SecondTheread st = new SecondThread;

             把st,即Runnable的线程实例包装成Thread类的:new Thread(st);

 调用Thread的Start()方法启动线程:new Thread (st).start();

实现Callable类,重写一个run()方法的方式实现多线程的创建。其代码基本思路同上,相当于是前两者的增强版(可以有返回值,可以声明抛出前异常,而前两个不可以)。但是其启动方式不一样,由于继承的类没有start()的线程启动方法,所以其启动方式也较为特殊:

 ●启动方法:①获取Callable接口的线程实例,包装成Futuretask(其是Runnable的实现类,相当于把Callable的线程包装成Runnable)

             再把Runnable的线程实例包装成Thread类。

 调用Thread的Start()方法启动线程。

四、线程状态转换图:

注:

●线程启动之后不是一启动就进入执行状态,而是要经过新建,就绪,运行,阻塞,和死亡五种状态。期间由于CPU需要在多条线程之间切换,于是运行状态就会多次在运行和阻塞之间切换。

●什么是运行和阻塞?

    当调用run()方法开始执行线程执行体,是就是运行状态,任何时候只有一条线程处于运行状态。

当进入阻塞时就是放弃现在正在使用的系统资源,给其他线程执行机会。

●下面情况线程会进入阻塞状态以及相应的会结束阻塞:

①线程调用sleep方法,->调用sleep方法经过了指定的时间则结束阻塞。

线程调用了一个阻塞的IO方法,在该方法返回之前,该线程被阻塞。->IO方法已经放回,则结束阻塞。

③线程试图获得一个同步监视器,但是监视器被其他线程所持有.->获得同步监视器,结束阻塞。

④线程真正等某个通知(notify)唤醒,而唤醒还没有来。->接收到其他线程的通知,结束阻塞。

⑤程序调用了线程的suspend方法将该线程挂起,不过这个方法容易导致死锁,所以一般不用。->使用resume方法恢复。

●线程的死亡:

①run()方法执行结束,线程正常结束。

②线程抛出未捕获的异常,或者ERROR。

③直接调用Stop方法强制结束,容易导致死锁。不建议使用。

如何设置线程为后台程序:调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程。

后台线程:如果所有的前台线程四死亡后,后台线程也会随之死亡。

要经某个线程设置为后台线程,应该在线程启动之前设置。

五、线程的安全:(同步代码块和同步方法实现)

●为什么需要线程安全?

因为run方法的方法体一般情况下不具有同步安全性,当多条线程并发访问,修改某一个共同资源时,为了避免修改同一个资源带来的不安全问题(经典问题,银行取钱),于是对其进行加锁,让其线程同步。

①实现线程安全的的方法一:同步代码块。

Synchronized (account)//account是同步锁的对象,也就是同步监视器,选择竞争资源。

{

//把代码块里的代码化为一体,不允许其运行时被其他线程打断。

}


②同步方法:同步方法就是使用synchronized关键字来修饰某个方法,则成为同步方法。其无需指定同步监视器,默认为this,也就是对象本省。

实现原理:当线程进入被同步监视锁监视的代码之前,线程获得同步监视器,保证任何时候只有一条线程进入同步监视器内执行代码,执行完后释放同步监视器。

●什么时候释放同步监视器?

①同步代码块执行完成

②代码块遇到break,return时。

③遇到未捕获的异常

④调用同步监视器额wait()方法。

▲线程安全与不安全的利弊?

线程安全:适合多线程访问,但是程序性能较低。

线程不安全:适合单线程程序,程序性能较好,但是不实用。

六、线程的通讯:

①如果不加控制,多个线程“自由的”并发执行。

②可以通过同步解决竞争资源的的问题

③希望线程有序执行,可以使用放置标识,旗标的方法

④wait()控制当前线程一直等待,释放同步锁,直到收到唤醒通知。

⑤notify()发送指定的线程的唤醒通知。

notifyAll()发送所有线程的唤醒通知。

七、线程组:对线程进行批量管理,调用Thread的

设置优先级:设置线程组的最高优先级,线程组内的优先级不会被改变,但是后面加入的线程的优先级不能高于原有的优先级。

八、线程池:系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互,在这样的情况下,使用线程池(缓存大量线程),以较好的提高性能。

●池的本质:就是一种缓存技术。

●缓存的本质:牺牲空间换取时间。

●Excutors工厂类来创建线程池。


九、常用的方法:

①isAlive(),判断线程是否死亡。

②THread的start()方法启动线程。

③Join()方法使相应线程必须先执行完成后才执行其他线程。

④sleep()方法是暂停当前线程,让其他线程有机会执行,不理会其他线程的优先级,大家获得执行的机会一样。

⑤yield()方法也是暂停当前的线程,但是其只会给优先级相同或者优先级更高的的线程执行机会。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值