多线程(基础)
Posted ohana!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程(基础)相关的知识,希望对你有一定的参考价值。
目录
一,线程的概念
Java中的线程,就是以轻量级的进程来实现的
二,进程和线程的区别/联系
(1)进程包含线程,一个进程至少包含一个线程
(2)进程是系统分配资源的最小单位(基本单位),线程是操作系统调度cpu执行的最小单位(基本单位)
(3)进程状态的改变会耗费很多的时间,线程相对来说更少
(4)进程占用独立的虚拟地址空间,同一个进程内的多个进程可以共享这个进程的内存
- 一个进程要访问另一个进程的数据:需要使用通信的方式(代价比较大)
- 同一个进程的线程,可以直接使用共享变量
- 一个进程挂掉,不会影响其他进程,但是一个进程中的线程挂掉,就可能影响整个进程挂掉
(5)线程也具有进程的特征
- 并发:一个CPU以时间片轮转调度算法,依次执行多个线程(肉眼感知同时进行)
- 并行:多个cpu在一个时间点,同时执行多个线程
三,线程的创建
(1)继承Thread,重写run方法
public class MyThread
private static class myThread extends Thread
@Override
public void run()
System.out.println("my thread run!");
public static void main(String[] args)
myThread thread = new myThread();
thread.run();
(2)实现Runnable接口,重写run方法
public class MyRunnable
private static class myRunnable implements Runnable
@Override
public void run()
System.out.println("my runnable run!");
public static void main(String[] args)
Thread thread = new Thread(new myRunnable());
thread.run();
(3)实现Callable接口
实际上的常用写法:
public class MyThread
public static void main(String[] args)
Thread thread = new Thread(new Runnable()
@Override
public void run()
System.out.println("线程启动了!");
);
thread.start();
四,多线程的优势/作用
(1)多个进程可以并发并行的执行,提高执行效率
(2)多线程技术使程序的响应速度更快 ,因为用户界面可以在进行其它工作的同时一直处于活动状态;
(3)当前没有进行处理的任务时可以将处理器时间让给其它任务;
(4)占用大量处理时间的任务可以定期将处理器时间让给其它任务;
(5)可以随时停止任务;
(6)可以分别设置各个任务的优先级以优化性能。
五,多线程的常用API
(1)activeCount
(返回当前进程处于活跃状态的线程数,线程创建好后,在销毁之前都是活跃态的)
(2)run
(定义线程需要执行的任务代码)
(3)start
(创建系统线程,并申请系统调度,转化为就绪态,如果调度到了,进入运行态,才会执行run方法的任务代码)
(4) currentTread
(返回这行代码执行时的当前线程引用对象)
(5)getName
(返回线程的名称)
(6)sleep
(让当前线程休眠???毫秒)
(操作系统内部还有一个阻塞队列,线程休眠/阻塞,就是进入这个队列,等恢复(sleep就 是等待一段时间)再拿出来执行)
(7) yield
(让当前线程让步,从运行态转为就绪态)
(8)isAlive
(线程引用对象是否是活跃状态)
(9) setDaemon
(设置某个线程为守护线程)
(10)isDaemon
(检测是否是守护线程,非守护线程就不会结束)
(11) isInterrupt
(返回线程内置的中断标志位)
(12)interrupt
(表示中断这个动作)
(13) join
(当前线程假如另一个线程,当前线程在这行代码等待,等另一个线程执行完,在执行后面的代码)
(14)join(可以理解为超时等待)
六,线程安全
(1)对于多个线程,操作同一个共享数据
(堆里面的对象,方法区中的数据,如静态变量)
- 如果都是读操作,没有赋值操作,只是获取值——没有安全问题
- 如果一个读,一个写
- 多个写
只要有一个线程在进行写操作,就会存在线程安全问题
(2)产生线程安全的原因
1.原子性
(表示一组操作,可能是一行代码或者多行代码,是不可拆分的最小执行单位,就表示这组操作时原子性的)
2.可见性
(一个线程对共享变量值的修改,能够及时地被其他线程看到)
- 线程之间的共享变量存在 主内存 (Main Memory).
- 每一个线程都有自己的 "工作内存" (Working Memory) .
- 当线程要读取一个共享变量的时候, 会先把变量从主内存拷贝到工作内存, 再从工作内存读取数据.
- 当线程要修改一个共享变量的时候, 也会先修改工作内存中的副本, 再同步回主内存
由于每个线程有自己的工作内存, 这些工作内存中的内容相当于同一个共享变量的 "副本". 此时修改线程1 的工作内存中的值, 线程2 的工作内存不一定会及时变化
3.顺序性
也就是代码顺序,因为多线程具有并发并行的特征,代码执行的结果并不一定使我们想象的结果
七,解决线程不安全的问题
1.synchronized关键字
加锁方式
1) 直接修饰普通方法: 锁的 SynchronizedDemo 对象
2) 修饰静态方法: 锁的 SynchronizedDemo 类的对象
3) 修饰代码块: 明确指定锁哪个对象
同步解释
- 使用对象来枷锁,多个线程需要先申请锁(synchronized自动申请)
- 代码块结束(自动释放锁)
- 多个线程只能有一个线程获取到同一个对象的锁
底层原理
- 基于对象头加锁的方式,对象有对象头这一块区域,其中有一个字段标识是否加锁
- 一个对象,同一个时间只能有一个线程获取到该对象的锁
- jvm基于对象头加锁:monitor lock监视器锁
- 本质上会使用到操作系统mutex lock锁来实现
作用
- 保证了原子性,可见性,顺序性
2.volatile关键字
语法
- 修饰一个变量
作用
- 保证可见性,有序性
注意
- 不保证原子性
适用场景
- 读操作:读操作本身就是原子性的,所以使用volatile这行语句就是线程安全的
- 写操作:赋值操作是一个常量值,也可以保证线程安全(写到主存)
3.Lock
Lock来源于juc包:java.util.concurrent包,这个包下的所有类,都是提供多线程并发编程用的,且满足线程安全,效率也很高
ReentrantLock这里的reentrant的意思就是可重入的
作用:
jdk提供锁的对象,专门用来加锁,达到线程安全的操作(synchronized的方式是自动加锁,释放锁,而Lock相当于是请了一个保安公司的保安来达到线程安全)
使用方式:
Lock lock = new ReentranLock();
try
lock.lock();//锁对象加锁,只能有一个线程获取到锁
........//需要保证线程安全的代码
finally
lock.unlock();//不管是否出现异常,都要释放锁
synchronized和Lock的区别:
1)从语法看,synchronized是自动加锁和释放锁,而Lock是显式(手动)来加锁释放锁;Lock更灵活,但要保证始终要释放锁(执行完不管是否出现异常)
2)Lock提供了更多获取锁的方式
3)从效率看,线程冲突比较严重的时候,lock性能要高很多
以上是关于多线程(基础)的主要内容,如果未能解决你的问题,请参考以下文章