-
多线程与单线程的区别?
单线程下,任务是排队执行的,也就是同步,就像是在cmd下输入一条命令后, 必须等这条命令执行完才可以执行下一条命令;而多线程是异步的,线程被调用的时机是随机的,因此代码的运行结果与代码的执行顺序或是调用顺序(执行start()方法的顺序) 是无关的。 -
实现多线程编程的方式:
一共有两种: 1.继承Therad类 2.实现Runnable接口
使用Thread类的方式创建新线程时,最大的局限性是不支持多继承,因为Java语言的特点是单根继承,所以为了多继承,可以用实现Runnable接口的方式。 -
调用start()启动线程和run()启动线程的区别?
调用start()方法会通知“线程规划器”,此线程已经准备就绪,等待调用线程对象的run()方法;而直接调用run()方法就不是异步执行而是同步执行,此时线程对象并不是交给“线程规划器”处理而是main主线程来调用run()方法,相当于是主线程下的一个顺序执行的任务,并不会在多线程下工作。 -
多线程的执行是异步的,通过加锁实现同步,在任意时刻保证只有一个对象执行,避免线程不安全问题(多个线程对同一个对象中的实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程,取到的数据可能是被修改过的数据,产生脏读),加锁的代码称为“互斥区”或“临界区”。
-
停止线程的方式?
Java中一个有三种方式终止正在运行的线程
1.使用退出标志,使线程正常退出,也就是当run()方法完成后线程终止
2.使用stop()方法强行终止线程
3.使用interrupt()方法中断线程(interrupt()方法并不能中断一个线程,需要和isInterrupted()方法或interrupted()方法配合使用,利用return或是异常处理来终止线程)
public static boolean interrupted();会返回中断标志状态并改变中断标志
public boolean isInterrupted();仅仅返回中断标志的状态
如果在sleep状态下停止某一线程,会进入catch语句,并清除停止状态值,使之变成false
-
暂停线程
使用suspend()方法暂停线程,使用resume()方法恢复线程的执行。这两个方法如果使用不当,极易造成公共同步对象的独占,使其他线程无法获得公共同步对象,而且容易导致因为线程暂停而导致数据不同步的情况。 -
线程的优先级
线程的优先级分为1–10这10个等级,线程的优先级具有继承性、随机性(优先级较高的线程不一定会先被执行) -
守护线程
Java线程中有两种线程,一种是用户线程,一种是守护线程。守护线程是一种特殊的线程,他的特性具有陪伴的含义,当线程中不存在用户线程了,守护线程就会自动销毁。典型的守护线程是垃圾回收线程。 -
非线程安全
多个线程对同一个对象中的实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程,取到的数据可能是被修改过的数据,产生脏读。
非线程安全问题存在于实例变量中,如果是方法内部的私有变量,则不存在“非线程安全问题” -
synchronized锁
关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法当作锁,对于同一个对象被多个线程调用的情况下,哪个线程先执行,哪个线程就持有该方法所属对象的锁,其他线程只能处于等待状态。如果多个线程访问多个对象的对象锁,那么JVM就会创建多个锁。同一个对象的不同线程调用synchronized关键字声明的方法一定是排队运行的。
synchronized关键字拥有锁重入的功能,当一个线程获得一个对象锁以后,再次请求此对象锁是可以再次得到该对象锁的,因此一个synchronized方法的内部调用本类其他synchronized方法时,是可以得到锁的。 -
继承中的synchronized的注意点
存在父子继承关系时,子类可以通过可重入锁调用父类的同步方法。
同步不具有继承性,想要在子类中实现同步,还是需要在子类中添加synchronized关键字
对象锁
-
synchronized同步语句块
synchronized代码块是对某一个对象加锁。不在synchronized代码块中就是异步执行,在synchronized代码块中就是同步执行。
1.synchronized(this)是对当前对象加锁
2.synchronized(Object obj)是对指定实例对象加锁
如果每次上锁的实例对象不同,就会创建多个锁,线程的执行还是异步的;如果每次上锁的是同一个实例对象(同一个常量池String实例对象或是同一个Object对象(没有new新的对象)),并且由同一个对象的不同线程获取锁,那么这些线程拿到的就是同一把锁,即使用的“对象监视器”是同一个,就会使其他线程阻塞,排队执行同步代码块;;synchronized(非this)代码块锁住的不是this对象锁,因此不会阻塞其他线程执行同步方法或是synchronized(this)代码块。 -
synchronized同步方法
synchronized关键字声明在方法上,也是对this对象进行上锁;
全局锁
- synchronized(类名.class){}是对整个类的所有实例对象上锁
- synchronized关键字声明在staic方法上也是对整个类的所有实例进行上锁。
Class锁会对类的所有实例对象起作用,即只要是指定类的实例对象的线程要来获取锁(不同的对象调用不同的线程获取锁),都会对这些线程进行同步,阻塞,排队执行。
-
对于静态内部类的对象inner,如果使用synchronized(inner){}同步代码块实现同步,获取到inner对象 的锁后,会对所有(不论是不是相同对象)来请求获取静态内部类中静态同步方法(因为此时获取到的都是inner对象锁)锁的线程进行同步阻塞处理
-
同步方法容易造成死循环,一直由一个线程获取锁不释放,导致其他线程不能访问同步资源,因此可以采用同步代码块的非this对象来解决这样的问题,因为不同的非this对象拿到的是不同的锁,就不会被阻塞。
-
死锁
由于多个线程之间共享同一片资源,一个资源在任意时刻只能被一个线程获取(互斥条件),线程对于已获得的资源没有使用完之前不能被其他线程强行剥夺(不可剥夺条件),只有自己使用完毕之后才会释放,只有被释放的资源才能被其他线程获取,否则就会阻塞等待。如果一个线程对已有 的资源不释放,而又去尝试获取其他被占用的资源(请求与保持),那么几个线程之间的等待关系形成了一个环(环路等待),就会造成死锁。