首页 > 技术文章 > 线程方法

wdss 2019-04-23 20:52 原文

使用多线程原因:
1、当执行某个耗时任务时,需要开启多线程
2、希望多个任务“同时”执行
3、防止线程阻塞
4、完成某个特定的任务
 
 
进程:正在执行的程序

线程:具有完成特定任务的一条执行路径,是CPU执行的最小单位(数据传输的基本单位是字节)

注意:CPU在某个时间刻度上只能够执行一条原子性语句(字节最小是bit位)

原子性语句:不可再分割的语句
 
CPU执行原理
1、真实环境下,除了多核处理器,本质都是CPU在某一个时间点上执行了一条线程
2、CPU看起来像是同时执行,但是本质上只执行了一条线程的一条原子性语句
3、在Java的线程程序中,采用的是抢占式调度模式
4、并发和并行的区别:
 并发:在同一个时间段同时执行
 并行:在同一个时间刻度上同时执行,物理上同时执行
5、同步和异步的区别:
 同步:本质就是串行执行,并发情况下会出现同步问题
 异步:能够在处理一个任务的同时还能够处理其他事情
多进程的好处:提高了进程的使用率,从而间接提高了CPU的使用率
多线程的好处:提高了CPU的使用率
 
一个Java进程至少包含了2个线程:主线程、垃圾回收线程。

----------------------------------------------------------------------------------
开启线程方式一:
继承Thread类
自定义类MyThread继承Thread类。
MyThread类里面重写run( )方法。
创建线程对象
启动线程
注意:
1、启动线程使用的是start( )方法,而不是run()方法
2、线程不能多次启动,否则会报异常
显示线程的名称:
Thread.currentThread( )表示当前正在执行的是哪一条线程那么就是对应的那个线程的名称
Thread.currentThread( ).getName( ) 获取当前正在执行的线程的名称
run( )线程的核心任务代码区,类似于main方法
继承Thread的优点:能够直接在子线程访问Thread类的方式(优先访问继承Thread类)
-----------------------------------------------------------------------------------
开启线程的方式二(开发中建议使用):
1、自定义类MyRunnable实现Runnable接口
2、重写run()方法
3、创建MyRunnable类的对象
4、创建Thread类的对象,并把步骤3创建的对象作为构造参数传递
5、启动线程
Runnable方式开启线程的好处:
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();
函数式接口:只有一个抽象方法的接口称为函数式接口(Runnable)
------------------------------------------------------------------------------------------------------
设置和获取线程的名称
通过构造方法
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
 
线程休眠:Thread.sleep(1000L) 阻塞当前正在执行的线程1s
 
线程中断:public final void stop()终止子线程的生命周期(已停用)

 public void interrupt()给子线程抛出一个异常,子线程还可以继续执行
 
后台线程:在程序运行的时候在后台提供的一种通用的服务的线程,并且这种线程并不属于程序中不可或缺的部分。
 只要有任何非后台线程还在运行,程序就不会结束。
 
线程加入:public final void join( )(t.join();//主要用于等待t线程运行结束)
线程礼让:public static void yield( )让出CPU执行权一小会,一小会不确定多久
 让出了CPU的执行权,释放了执行权,但还可以争夺。
===================================================================
同步线程
线程安全问题:在多线程环境下,存在多条线程使用多条原子性语句对共享数据做了写操作
解决办法:1、同步代码块   2、同步方法   3、Lock锁
同步代码块
格式:
 synchronized(对象){需要同步的代码;}
同步的好处:
 解决了多线程的安全问题
同步的弊端:
 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,
 降低了程序的运行效率。如果出现了同步嵌套,就容易产生死锁问题

锁对象:
 锁对象必须是多个线程同一把锁
 锁对象可以是任意对象
 
同步方法:
格式:
 public synchronized 返回值 方法名(参数列表){需要同步的代码块}

如果锁对象是this,就可以考虑使用同步方法。
方法分为两种:静态方法和非静态方法
非静态方法的锁对象:this
静态方法的锁对象:类的字节码文件对象
注意:synchronized可以修饰方法,修饰代码块,但是不能修饰构造方法、成员变量等。
 
Lock锁出现死锁现象
死锁指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象。
锁对象是任意对象,如果设计在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)延迟多长时间执行指定的任务到多长周期
 

推荐阅读