面试-多线程
面试-多线程
谈谈线程和进程
问:谈谈线程和进程
答:进程是操作系统分配资源的最小独立单位,相当于一个独立运行的程序(比如打开的微信、IDEA),每个进程都有自己专属的内存空间,进程之间相互隔离、互不干扰。而线程是操作系统调度执行的最小单位,它不能独立存在,必须依附于进程,一个进程可以包含多个线程,这些线程共享所属进程的全部资源。
Java实现多线程的方式
问:Java实现多线程的方式
答:继承Thread类重写run()方法
实现Runnable接口重写run()方法
线程池(通过管理线程的方式)
package com.tensoflow;
class MyThread extends Thread {
@Override
public void run() {
while (true) {
try {
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() + "执行中...");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class ThreadCreateDemo {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
// 必须调用start(),不能直接调用run()(直接调是普通方法,不启动新线程)
thread1.start();
thread2.start();
}
}
package com.tensoflow;
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable的线程:" + Thread.currentThread().getName());
}
}
public class RunnableCreateDemo {
public static void main(String[] args) {
// 创建Runnable实例,传给Thread
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
// 简化写法:匿名内部类
new Thread(() -> {
System.out.println("匿名线程:" + Thread.currentThread().getName());
}).start();
}
}
Thread类中的常用方法
问:Thread类中的常用方法
答:
start() 启动线程,并执行线程的run()方法
run() 线程的执行体
sleep() 让当前线程睡眠指定的毫秒数
setName() 设置线程名称
getName() 获取线程名称
setPriority(int priority) 设置线程的优先级
getPriority() 获取线程的优先级
如何让线程顺序执行
问:如何让线程顺序执行
答:使用join()方法把指定的线程加入到当前线程。
package com.tensoflow;
public class CccServiceApplication {
public static void main(String[] args) throws Exception {
Thread a = new Thread(() -> {
for(int i = 0; i < 10000; i++) {
System.out.println("aaa...");
}
});
Thread b = new Thread(() -> {
try {
a.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 0; i < 10000; i++) {
System.out.println("bbb...");
}
});
b.start();
a.start();
System.out.println("主程序");
}
}
wait()和sleep()有什么区别
问:wait()和sleep()有什么区别
答:从来源看:sleep() 来自Thread ,wait() 来自Object 。
从线程状态看:sleep() 导致当前线程进入timed_waiting状态,wait() 导致当前线程进入waiting状态。
从恢复执行看:sleep() 在指定时间之后,线程会恢复执行,wait() 则需要等待别的线程使用 notify()/notifyAll() 来唤醒它
守护线程是什么
问:守护线程是什么
答:守护线程是一种比较低级别的线程,一般用于为其他类别线程提供服务。因此当其它线程都退出时,它也就没有存在的必要了。例如JVM中的垃圾回收线程。主要应用有后台日志输出线程、后台监控线程(监控系统CPU、内存使用率、......)等。好处是仅在程序运行时需要,程序退出后自动终止无需手动关闭。
package com.tensoflow;
public class DaemonThreadDemo {
public static void main(String[] args) {
// 定义守护线程 b
Thread b = new Thread(() -> {
for (int i = 0; true; ++i) {
try {
// 放慢打印速度,便于观察
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i + " " + Thread.currentThread().getName() + " is running.");
}
}, "Daemon-Thread");
// 标记b为守护线程,必须写在start()方法之前
b.setDaemon(true);
// 启动线程
b.start();
// 主线程(用户线程)执行500ms后结束
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行完毕,JVM 即将退出!");
// 主线程结束后,守护线程b会被强制终止
}
}
谈谈ThreadLocal
问:谈谈ThreadLocal
答:可以把ThreadLocal视为一个普通变量,其与普通的变量之间的区别在于ThreadLocal变量只属于某一个线程。单个ThreadLocal变量默认只存一个值。需要存多个值可以在ThreadLocal泛型中写上一个对象,使用对象存储多个值。其能解决线程内跨方法传递私有数据等问题。另外使用线程池时每次使用完都需要remove,不然会导致内存泄露与数据错乱。
package com.tensoflow;
class Person{
String name = "zhangsan";
}
public class ThreadLocalTest {
static ThreadLocal<Person> t1 = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// null 即使第二个线程设置了new Person()
System.out.println(t1.get());
}).start();
new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
t1.set(new Person());
// zhangsan 只有自己的线程能拿到这个Person对象
System.out.println("线程二: " + t1.get().name);
}).start();
}
}
多线程怎么解决高并发
问:多线程怎么解决高并发
答:有两种。一种是Synchronized是利用锁的机制,使变量和代码块在某一时只能被一个线程访问。二是ThreadLocal其为每一个线程都提供变量的副本,使得每个线程在某一时间访问到的并不是同一对象,这样就隔离了多个线程对数据的数据共享。
