什么是线程?线程与进程及程序是什么关系
程序: 一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。为了实现某个功能,使用某种语言所写的静态指令
进程:指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体。程序加载至内存中
线程:进程内的一个相对独立的可执行的单元。若把进程称为任务的话,那么线程则是应用中的一个子任务的执行。
经典面试题:
线程和进程的区别是什么?
- 进程是操作系统分配资源的最小单元;
- 线程是操作系统调度的最小单元;
- 一个程序至少有一个进程;一个进程至少有一个线程
- 每个进程对应一个JVM实例,多个线程共享JVM里的堆;
- 线程不能看做独立应用,而进程可以;
- 进程有独立的地址空间,互不影响,而线程只是进程不同的执行路径;
- 进程的切换比线程的切换开销大;
如何创建并启动线程?
- 继承Thread类重写run()方法,调用start()方法即可启动一个线程
class Thread1 extends Thread{
@Override
public void run() {
// 书写线程所需要执行的逻辑代码
}
}
启动代码:
public class ThreadTest {
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
thread1.start();
}
}
- 实现Runnable接口
public class RunableTest implements Runnable{
@Override
public void run() {
// 书写线程中所需要执行的逻辑代码
}
public static void main(String[] args) {
new Thread(new RunableTest()).start();
}
}
- 实现Callable接口通过FutureTask包装器来创建Thread线程
public class RunableTest implements Callable<Integer>{
public static void main(String[] args) {
RunableTest runableTest = new RunableTest();
FutureTask<Integer> futureTask = new FutureTask<>(runableTest);
new Thread(futureTask).start();
}
@Override
public Integer call() throws Exception {
// TODO 书写所需要执行的逻辑代码
return null;
}
}
- 使用线程池的方式创建
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);
ThreadPoolExecutor executor = (ThreadPoolExecutor) pool;
executor.setCorePoolSize(15);
executor.execute(new Runnable() {
@Override
public void run() {
// TODO 书写所需要执行的逻辑代码
}
});
}
经典面试题:
实现多线程的方式有几种,他们有什么优劣?
答:在JDK5.0之后拥有四种实现方法:
1. 继承Thread类
- 代码简便易懂
- 但Java当中不支持多继承,继承Thread类意味将抛弃其他类特征的功能与扩展
2. 实现Runable接口
- Java当中支持多实现,实现与继承之间互不影响,可以更好的进行功能扩展
- 代码逻辑略复杂
3. 实现Callable接口通过FutureTask包装器来创建Thread线程
- 可以获得由线程执行任务过后的返回值。
4. 实现使用线程池创建
- 易于管理线程
- 便于对线程资源的合理分配及利用
- 线程执行完成之后不会立即消亡,减少对系统资源的消耗
同步锁
什么情况下需要?
多个线程拥有一个共享资源(就会存在数据安全问题)
实现同步锁的方式
- synchronized
译为:同步
将多个线程所共享的数据或资源进行同步处理,即线程一进入,在线程一未处理完成之前不允许任何线程进入。
synchronized使用方法
同时synchronized除了可以使用代码快的方式也可以作用在方法中synchronized (使用者对象){ // 需要同步进行的代码 } // 实例代码 synchronized (this) { }
注意事项:public synchronized test () { // 需要同步进行的代码 }
- synchronized 锁住共享资源,要求所有线程使用的为共享资源,而非单个资源
- 当synchronized 修饰代码快时,它锁住的是this,即当前对象
- 当synchronized 修饰静态方法时,它锁住的是类,非静态方法时为this,即当前类对象
- synchronzied使用不当时,会产生死锁情况。
例如:A线程先使用a资源并上锁,再使用b资源并上锁,结束后释放。B线程先使用b资源并上锁,再使用b资源并上锁,结束后释放。
当A线程锁住a对象,并未处理完成时,B线程也开始工作,锁住了b资源。
由于B线程锁住了b资源导致,A无法访问b资源,因此一直在等待B线程释放。
而A线程锁住了B所需要的a资源,B无法反问a资源,因为一直在等待A线程释放。
实例代码:
public class RunableTest {
private static String a = "10";
private static String b = "20";
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);
ThreadPoolExecutor executor = (ThreadPoolExecutor) pool;
executor.setCorePoolSize(15);
executor.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (RunableTest.a) {
a = "lock1";
System.out.println(a);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (RunableTest.b) {
System.out.println(b);
}
}
}
});
executor.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (RunableTest.b) {
b = "lock2";
System.out.println(b);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (RunableTest.a) {
System.out.println(a);
}
}
}
});
}
}
为了避免死锁情况的产生,在使用时候,要考虑清楚
2. Lock
译为:锁
所在包:java.util.concurrent.locks
Lock为接口,即我们要使用它的实现类ReentrantLock,代码如下:
在ReentrantLock当中提供了两个构造器,一个空参构造器,一个有参构造器。
有参构造器传入的值为是否采用公平模式,即先进先出。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
使用Lock代码:
Lock lock = new ReentrantLock();
lock.lock();// 上锁
// 需要锁住的代码
System.out.println("HelloWorld");
lock.unlock();// 解锁
经典面试题
synchronized与Lock的不同?
不同:
- Lock需要手动释放锁,而synchronized自动释放锁
- synchronized是Java当中的关键字,而Lock为接口
- synchronized在遇到异常时会自动退出并释放锁,而Lock出现异常不会自动释放锁,因此Lock.unlock()解锁操作一般写在finally块当中
附线程生命周期图及相关方法说明:
相关方法说明:
sleep():让线程休息指定毫秒数
start():启动一个线程(注意:启动一个线程并非直接运行,而是等待CPU分配)
join():让XX线程先运行完成之后再执行此线程,可理解为现实生活的插队
yield():让线程暂停
Object.wait(): 让当前线程进入等待状态,注意此方法在Object类中,而非Thread类中
notity()/notityAll():唤醒一个线程或多个线程
以上线程章节学习完结,如有不对之处,敬请指出~~