1、当执行某个耗时任务时,需要开启多线程
2、希望多个任务“同时”执行
3、防止线程阻塞
4、完成某个特定的任务
线程:具有完成特定任务的一条执行路径,是CPU执行的最小单位(数据传输的基本单位是字节)
注意:CPU在某个时间刻度上只能够执行一条原子性语句(字节最小是bit位)
原子性语句:不可再分割的语句
2、CPU看起来像是同时执行,但是本质上只执行了一条线程的一条原子性语句
3、在Java的线程程序中,采用的是抢占式调度模式
4、并发和并行的区别:
并发:在同一个时间段同时执行
并行:在同一个时间刻度上同时执行,物理上同时执行
5、同步和异步的区别:
同步:本质就是串行执行,并发情况下会出现同步问题
异步:能够在处理一个任务的同时还能够处理其他事情
多线程的好处:提高了CPU的使用率
----------------------------------------------------------------------------------
开启线程方式一:
继承Thread类
自定义类MyThread继承Thread类。
MyThread类里面重写run( )方法。
创建线程对象
启动线程
1、启动线程使用的是start( )方法,而不是run()方法
2、线程不能多次启动,否则会报异常
Thread.currentThread( )表示当前正在执行的是哪一条线程那么就是对应的那个线程的名称
Thread.currentThread( ).getName( ) 获取当前正在执行的线程的名称
开启线程的方式二(开发中建议使用):
1、自定义类MyRunnable实现Runnable接口
2、重写run()方法
3、创建MyRunnable类的对象
4、创建Thread类的对象,并把步骤3创建的对象作为构造参数传递
5、启动线程
1、可以避免Java单继承带来的局限性
2、适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码、数据有效分离,较好的体现了面向对象的设计思想
开启线程方式三:实现Callable方式开启线程(本质还是方式二,需要依赖一个类FutureTask )
public class FutureTask<V> implements RunnableFuture<V> { }
public interface RunnableFuture<V> extends Runnable, Future<V> { void run( ); }
public interface Callable<V> { V call() throws Exception; }
方式三开启线程的优点:
1、call方法有返回值,run方法没有返回值,可以返回结果给主线程
2、call方法可以抛出异常告知主线程(线程通信)
缺点:会导致主线程在计算完成之前阻塞
开启线程方式四:本质是一个继承或者实现了某个类或者接口的子类匿名对象(在Android使用频繁)
注意:如果同时使用两种方式开启线程,继承Thread方式优先!!!
匿名内部类开启线程的好处:线程任务执行完毕就是垃圾对象,等待垃圾回收器空闲的时候回收,节约内存资源
Lambda表达方式开启线程
new Thread( ()->{
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
} ).start();
------------------------------------------------------------------------------------------------------
通过构造方法
Thread(String name) 分配新的 Thread 对象。
Thread(Runnable target, String name) 分配新的 Thread 对象。
通过线程的成员方法
public final String getName( )
public final void setName(String name)
通过静态方法
public static Thread currentThread( ) 可以获取任意方法所在的线程名称
可以获取主线程的线程名称:Thread.currentThread( ).getName( ) ;
获取线程的id: long getId( )
public final int getPriority( )
public final void setPriority(int newPriority)
优先级1~5~10
public void interrupt()给子线程抛出一个异常,子线程还可以继续执行
只要有任何非后台线程还在运行,程序就不会结束。
让出了CPU的执行权,释放了执行权,但还可以争夺。
===================================================================
线程安全问题:在多线程环境下,存在多条线程使用多条原子性语句对共享数据做了写操作
解决办法:1、同步代码块 2、同步方法 3、Lock锁
格式:
synchronized(对象){需要同步的代码;}
同步的好处:
解决了多线程的安全问题
同步的弊端:
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,
降低了程序的运行效率。如果出现了同步嵌套,就容易产生死锁问题
锁对象:
锁对象必须是多个线程同一把锁
锁对象可以是任意对象
格式:
public synchronized 返回值 方法名(参数列表){需要同步的代码块}
如果锁对象是this,就可以考虑使用同步方法。
方法分为两种:静态方法和非静态方法
非静态方法的锁对象:this
静态方法的锁对象:类的字节码文件对象
死锁指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象。
锁对象是任意对象,如果设计在Thread里面,那就定死了,所以和锁有关的方法设计在Object类中
线程池里的每一个线程代码结束后并不会死亡,而是再次回到线程池中称为空闲状态,等待下一个对象来调用。
Executors工厂类来生产线程池
调用构造方法:public static ExecutorService newCachedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor( )
-------------------------------------------------------------------------------------------------------------
特点:可以在指定的时间执行指定的任务,同时还可以延时执行或者反复执行
定时器类:Timer
定时任务类:TimerTask
void schedule(TimerTask task, Date time)在指定的时间执行某个任务
void schedule(TimerTask task, Date firstTime, long period)在指定的时间执行指定的任务
void schedule(TimeTask task, long delay)延时多长时间执行指定的任务
void schedule(TimeTask task, long delay, long period)延迟多长时间执行指定的任务到多长周期