Java线程相关知识

1.什么是进程?
它是运行中的程序,是动态的概念。每个进程有独立的资源空间。
2.什么是线程
线程,称为轻量级进程,是程序执行流的最小单元,是程序中一个单一的顺序控制的流程。线程是进程的一个实体,是被系统独立调度和分配的基本单位。
单个程序中可以同时运行多个不同的线程执行不同的任务。
3.多线程的特点
一个进程可以包含一个或者多个线程。
一个程序实现多个代码同时交替运行需要产生多个线程。
线程的本身不拥有系统资源,与同一个进程的其他的线程共享再进程拥有的资源。
同一进程中的多个线程之间可以并发执行。CPU会在随机抽出时间,让程序一会做这个,一会做另一件事情。
多线程的目的:就是最大限度的利用CPU资源。

Java运行系统再很多方面依赖于多线程,所有的类库设计都考虑到多线程。Java是纯面向对象语言,Java线程模型也是面向对象的。


4.实现线程的两种方式:
通过继承Thread类创建线程
普通Java类如果继承了Thread类就成为一个线程类。并通过该类的start()方法来驱动线程,执行线程的代码。Thread类的子类可以直接实例化,但在子类中必须覆盖run方法,才能真正运行线程代码。
通过实现Runnable接口创建线程
实现Runnable接口的必须借助线程类才能创建线程。通过Runnable接口创建线程分为两步①创建实现Runnable接口的类的实例。②创建一个Thread类对象,将一步实例化得到的Runnable对象作为参数传入Thread的构造方法,通过Thread类的start方法启动线程。

start()方法金和run()方法的区别
start()方法来启动线程,真正实现了多线程运行。这时无序等待run方法体代码执行完毕,可以直接执行下面的状态,通过调用线程类的Start方法来启动一个线程,这时,此线程是处于就绪状态,并没有运行。然后通过此Thread类调用方法run()来完成其运行的操作的,这里方法体run称为线程体,它包含要执行这个线程的内容,Run方法运行结束,此线程终止。然后CPU再调度其他线程。
run()普通方法的方法体。
实现Runnable接口比继承Thread类所具有的优势:
适合多个相同的程序代码区处理同一个资源
可以避免java的单继承的限制
增强程序的健壮性,代码可以被多个线程共享,代码和数据独立。
线程池只能放入实现Runnable或callable类线程,不能直接放入继承Thread的类
5.线程的生命周期
线程也同样要经历新建,就绪,运行,阻塞,和死亡
创建状态:在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
就绪状态:当调用了线程对象的start方法之后,该线程就处于就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态,在线程运行之后,从等待或睡眠中回来之后,也会处于就绪状态。
运行状态:线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入运行状态,开始运行run函数当中的代码。
阻塞状态:线程正在运行的时候,被暂停,通常是为了等待某个事件发生之后在继续运行,比如获得锁资源,sleep,suspenf,wait方法都可以导致线程阻塞。
死亡状态:如果一个线程的run方法执行结束或者调用stop方法后,该线程已经死亡的线程,无法再使用start方法命令其进入就绪。
Java线程有优先级,取值范围事1到10Thread类有以下三个静态常量。
static int MAX_PRIORITY :线程具有最高优先级 值为10
static int MIN_PRIORITY 线程具有最低优先级 值为1
static int NORM_PRIORITY 分配给线程的默认优先级 值为5

上图的一些线程关联的方法简介:
sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
join():指等待的线程终止。在线程包含的情况下,子线程加了join()方法之后,主线程一定要等到子线程结束之后才结束主线程。
yield()方法作用事:暂停在当前正在执行的线程对象,并执行其他线程。与sleep的区别是一个进入阻塞状态,一个进入可运行的状态。与其他线程一起争取资源。
setPriority(上面静态常量):更改线程的优先级。
interrupt();不要以为它是中断某个线程!它只是线程发送一个信号,让线程在无限等待的时候能抛出就抛出,从而结束线程,但是如果你吃掉这个异常,那么这个线程是不会中断的。
sleep(): 强迫一个线程睡眠N毫秒。 
   isAlive(): 判断一个线程是否存活。 
   join(): 等待线程终止。 
  activeCount(): 程序中活跃的线程数。 
   enumerate(): 枚举程序中的线程。 
    currentThread(): 得到当前线程。 
   isDaemon(): 一个线程是否为守护线程。 
   setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束) 
  setName(): 为线程设置一个名称。 
   wait(): 强迫一个线程等待。 
  notify(): 通知一个线程继续运行。 
   setPriority(): 设置一个线程的优先级。
sleep()和wait()的区别:sleep睡眠时,保存对象锁,以及占用该锁,而wait释放对象锁。
6.为什么要线程同步?
线程同步事为了防止多个线程访问同一个数据对象事对数据造成的破坏。线程的同步事保证线程安全访问竞争资源的一种手段。
线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
对于同步,要时刻清醒在哪个对象上同步,这是关键。
编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
Java中的每一个对象都有一个内置锁。
7.synchronized作用于某个对象实例内,synchronized Meyhod(){}可防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其他线程不能同时访问这个对象人格一个synchronized方法)这时,不同的对象实例的synchronized方法时不相干扰的,其他线程照样可以访问相同类的另一个对象实例中的synchronized方法。
synchronized static 类名防止对各线程同时访问这个类中的synchronized static方法,它可以对类的所有对象实例起作用。
除了方法钱用synchronized关键字,synchornized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问 synvhronized(对象){}它的作用域时当前对象。
synchronized关键字时不能继承的,也就是说,父类的带有关键字的方法,继承的方法不带关键字的
缺点:不会自动放弃锁,发送异常的时候,jvm会释放所有的锁

8.Lock接口
解决获取锁的等待问题
读写锁的分离
其他锁的操作

常用的方法:
lock() 获取锁 (发生异常不自动释放锁;如果没有获取会等待)
trylock()获取锁(带有boolean型的返回值,无论是否成功立即返回不进行等待)
tryLock(long time, TimeUnit unit) 
特点:可以设置获取锁的等待时间,如tryLock(4, TimeUnit.SECONDS)等待4秒;可以在等待过程中相应中断; 
lockInterruptibly() 
特点:当一个使用该方法获取锁的线程没有获取到锁处于阻塞等待状态时,可以调用线程的interrupt()方法中断等待。 
9.线程池
提交Runnable,任务完成后Future对象,对象返回null
调用excute,提交任务,匿名Runnable重写run方法,run方法里是业务逻辑。
提交Callable,该方法返回一个Future实例表示任务的状态
调用submit提交任务,匿名Callable重写call方法,有返回值,获取返回值会阻塞,一直要等到线程任务返回结果。

线程池的种类:
Single Thread Executor :只有一个线程的线程池,因此所有提交的任务是顺序执行。
Cached Thred Pool :线程池有很多线程需要同时执行,老的可用线程将被新的任务触发重新执行,如果线程超过60秒内没执行,那么将被终止并从池中删除。
Fixed Thread Pool :拥有固定的线程数的线程池,如果没有任务执行,那么线程会一直等待。
Scheduled Thread Pool :用来调度即将执行的任务的线程池,可能不是直接执行,每隔多久执行一次。
Single Thread Schedule Pool:只有一个线程,用来调度任务在指定时间执行。

10.线程类经常遇到的面试题
①reentrantLock和synchronied的区别
reentrantLock性能高于synchronized锁。使用可重入锁需要手动unlock,灵活性较高,而且故名思义,它可以多次加锁,线程可以多次持有可重入锁。synchronized也是可重入锁。
reentrantLock的锁中断可以解决死锁问题,当线程interrupt中断时,该线程会放弃对锁的申请,从而解决死锁的问题。
reentrantLock可以限时锁申请,通过tryLock方法设置线程最长等待时间。
reentrantLock使用公平锁,和syncronized不同,并非随机响应线程的申请,而是根据先来后到。
②volatile的使用
当一个共享变量被volatile修饰时,他会保证修改的值会立即更新代主内存中去,其他线程需要读取时,它会去内存中读取新值。禁止进行指令重排的。不能保证原子性。
③failfast机制
一种iterator的机制,多线程环境下多个线程对一个集合进行遍历/修改。hashmap的迭代器是fail-fast的,hashtable则不是,因为hashtable在对元素的修改会先做一根集合拷贝,没有并发问题。
④线程池的作用
提高线程的可管理性,降低资源消耗,提高性能。
⑤CAS的实现原理以及问题
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B.当且仅当预期值和修改的新值相同时,将内存值V修改为B,否则什么都不做。
非阻塞算法:一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。
三大问题:ABA问题,加入版本号就可以了。循环时间长开销大,只能保证一个共享变量的原子性。
⑥Java中的ThreadLocal通常时在什么情况使用的?
首先要理解ThreadLocal的原理,每个Thread对象内部有个ThreadLocalMap,当线程访问ThreadLocal对象时,会在线程内部的ThreadLocalMap新建一个Entry,这样的话每个线程都有一个对象的副本,保证了并发场景下的线程安全。我理解ThreadLocal的使用场景是某些对象在多线程并发访问时可能出现问题,比如使用SimpleDataFormat的parse()方法,内部有一个Calendar对象,调用SimpleDataFormat的parse()方法会先调用Calendar.clear(),然后调用Calendar.add(),如果一个线程先调用了add()然后另一个线程又调用了clear(),这时候parse()方法解析的时间就不对了,我们就可以用ThreadLocal<SimpleDataFormat>来解决并发修改的问题。另一种场景是Spring事务,事务是和线程绑定起来的,Spring框架在事务开始时会给当前线程绑定一个Jdbc Connection,在整个事务过程都是使用该线程绑定的connection来执行数据库操作,实现了事务的隔离性。Spring框架里面就是用的ThreadLocal来实现这种隔离。
⑦AQS实现原理
AbstractQueuedSynchronzier(AQS),抽象式队列的同步器。定义了一套多线程访问共享资源的同步器。维护了一个volatile int state(代表共享资源),和一个FIFO线程等待队列,多线程曾用资源被阻塞时会进入此队列。AQS定义两种资源共享方式。共享(多个线程可同时执行)独占(只有一个线程能执行)
isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
   以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。
   再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。
⑧死锁代码实现
public class DieLock extends Thread { // 设置一个旗标,控制线程的执行与停止 private Boolean flag; public DieLock(Boolean flag){ this.flag = flag; } @Override public void run(){ if(flag){ synchronized (MyLock.objA){ System.out.println("if A"); synchronized (MyLock.objB){ System.out.println("if B"); } } }else { synchronized (MyLock.objB){ System.out.println("else B"); synchronized (MyLock.objA){ System.out.println("else A"); } } } }}

public static void main(String[] args) { DieLock dl1 = new DieLock(true); DieLock dl2 = new DieLock(false); dl1.start(); dl2.start(); }

⑩ 生产者消费者代码实现
import java.util.LinkedList;

public class Storage
{
// 仓库最大存储量
private final int MAX_SIZE = 100;

// 仓库存储的载体
private LinkedList<Object> list = new LinkedList<Object>();

// 生产产品
public void produce(String producer)
{
synchronized (list)
{
// 如果仓库已满
while (list.size() == MAX_SIZE)
{
System.out.println("仓库已满,【"+producer+"】: 暂时不能执行生产任务!");
try
{
// 由于条件不满足,生产阻塞
list.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}

// 生产产品
list.add(new Object());

System.out.println("【"+producer+"】:生产了一个产品\t【现仓储量为】:" + list.size());

list.notifyAll();
}
}

// 消费产品
public void consume(String consumer)
{
synchronized (list)
{
//如果仓库存储量不足
while (list.size()==0)
{
System.out.println("仓库已空,【"+consumer+"】: 暂时不能执行消费任务!");
try
{
// 由于条件不满足,消费阻塞
list.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
list.remove();
System.out.println("【"+consumer+"】:消费了一个产品\t【现仓储量为】:" + list.size());
list.notifyAll();
}
}

public LinkedList<Object> getList()
{
return list;
}

public void setList(LinkedList<Object> list)
{
this.list = list;
}

public int getMAX_SIZE()
{
return MAX_SIZE;
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值