1 进程和线程的区别
1.1 进程和线程的由来
1. 串行:初期的计算机智能串行执行任务,并且需要长时间等待用户输入。
2. 批处理: 预先将用户的输入集成清单,批量串行处理用户指令,仍然无法并发处理。
3. 进程:进程独占内存空间,保存各自运行状态,相互间不干扰且可以相互切换,为并发处理任务提供了可能。
4. 线程:共享进程的内存资源,相互间切换更更快速,支持更细粒度的任务控制,使线程的子任务得以并发执行。
1.2 进程是资源分配的最小单位,线程是CPU调度的最小单位。
1. 所有与进程相关的资源,都被记录在PCB中 。PCB(进程控制块)
2. 进程是抢占处理机的调度单位(进程拥有完整的虚拟内存地址空间);线程是属于某个进程,共享其资源。(同一进程内的不同线程共享同一地址空间,线程与资源分配无关,它属于某个进程,并与进程内的其他线程共享资源)
3. 线程只由堆栈寄存器、程序计数器和TCB组成。(寄存器可被用来存储线程内的局部变量,但不能存储其他线程的相关变量)
1.3 进程和线程的区别
1 线程不能看做独立的应用,而进程可以看做独立应用。
2 进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径。
3 线程没有独立的地址空间,多进程的程序比多线程程序健壮。
4 进程的切换比线程切换的开销大。
1.4 java 进程和线程的关系
1. java对操作系统提供的功能进行封装,包括进程和线程。
2.运行一个程序会产生一个进程,进程至少包括一个线程。
3. 每个进程对应一个JVM实例,多个线程共享JVM里的堆。
4. java采用单线程编程模型 ,程序会自动创建主线程。
5. 主线程可以创建子线程,原则上要后于子线程完成执行。
2 Thread中的start和run方法的区别
例:使用功能run方法时,程序主线程和Attck方法线程都是使用的主线程Main。
package Thread;
public class ThreadTest {
private static void attck(){
System.out.println("Finght");
System.out.println("Current Thread is:"+Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t =new Thread(){
public void run(){
attck();
}
};
System.out.println("Current Mian Thread is:"+Thread.currentThread().getName());
t.run();
}
}
结果为:
Current Mian Thread is:main
Finght
Current Thread is:main
当使用start方法时。程序主线程使用的线程为Main ,Attck方法线程使用的线程为Thread-0,一个新的线程。
package Thread;
public class ThreadTest {
private static void attck(){
System.out.println("Finght");
System.out.println("Current Thread is:"+Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t =new Thread(){
public void run(){
attck();
}
};
System.out.println("Current Mian Thread is:"+Thread.currentThread().getName());
t.start();
}
}
结果为
Current Mian Thread is:main
Finght
Current Thread is:Thread-0
总结:
1 调用start()方法会创建一个新的子线程,并启动。
2 run()方法知识Thread的一个普通方法的调用。
3 Thread 和 Runnable是什么关系
3.1.创建一个线程Mythread,继承Thread。
public class MyThread extends Thread {
private String name;
public MyThread(String name){
this.name=name;
}
@Override
public void run(){
for (int i = 0; i <10 ; i++) {
System.out.println("Thread start:"+this.name+",i="+i);
}
}
}
使用创建的类Mythread,达到多线程执行
public class ThreadDemo {
public static void main(String[] args) {
MyThread myThread1 = new MyThread("Thread1");
MyThread myThread2= new MyThread("Thread2");
MyThread myThread3 = new MyThread("Thread3");
myThread1.start();
myThread2.start();
myThread3.start();
}
}
3.2 使用Runnable 实现线程,创建Runnable的实现类MyRunnable
public class MyRunnable implements Runnable{
private String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run(){
for (int i = 0; i <10 ; i++) {
System.out.println("Thread start:"+this.name+",i="+i);
}
}
}
实现类MyRunnable,通过Thread线程执行。
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable("Runnable1");
MyRunnable runnable2= new MyRunnable("Runnable2");
MyRunnable runnable3 = new MyRunnable("Runnable3");
Thread t1=new Thread(runnable1);
Thread t2=new Thread(runnable2);
Thread t3=new Thread(runnable3);
t1.start();
t2.start();
t3.start();
}
}
总结:
1 Thread是实现了Runnable 接口的类,使Run支持多线程。
2 因类单线程继承原则,推荐多使用Runnable接口。
3.3 如何给Run()方法进行传参
1 构造函数传参
2 成员变量传参
3 回调函数传参
4 如何实现处理线程的返回值
4.1 实现的方式主要有三种 :
1 主线程等待法。 (让主线程循环等待,直到目标子线程返回值为止)
public class CycleWait implements Runnable {
private String value;
public void run(){
try{
Thread.currentThread().sleep(5000);
}catch (InterruptedException e){
e.printStackTrace();
}
value="we have data now";
}
public static void main(String[] args) {
CycleWait cw=new CycleWait();
Thread t=new Thread(cw);
t.start();
System.out.println("value:"+cw.value);
}
}
上述程序的执行结果是:value:null 。因为主线程执行完 t.start()之后,就直接回执行下一句,不去等待t 线程的执行结果,因此value的值认为空。修改程序:就是主线程等待法
public class CycleWait implements Runnable {
private String value;
public void run(){
try{
Thread.currentThread().sleep(5000);
}catch (InterruptedException e){
e.printStackTrace();
}
value="we have data now";
}
public static void main(String[] args) throws InterruptedException {
CycleWait cw=new CycleWait();
Thread t=new Thread(cw);
t.start();
while (cw.value==null){
Thread.currentThread().sleep(100);
}
System.out.println("value:"+cw.value);
}
}
2 使用Thread类的Join()阻塞当前线程以等待子线程完成处理。
public class CycleWait implements Runnable {
private String value;
public void run(){
try{
Thread.currentThread().sleep(5000);
}catch (InterruptedException e){
e.printStackTrace();
}
value="we have data now";
}
public static void main(String[] args) throws InterruptedException {
CycleWait cw=new CycleWait();
Thread t=new Thread(cw);
t.start();
t.join();
System.out.println("value:"+cw.value);
}
}
3 通过callable接口实现:通过Future Task 或者 线程池获取
通过Future Task 获取返回值:先实现一个callable接口的实现类
public class Mycallable implements Callable<String> {
@Override
public String call() throws Exception{
String value="test";
System.out.println("Ready to work");
Thread.currentThread().sleep(5000);
System.out.println("task Done");
return value;
}
}
通过 Future Task来获取线程返回值
public class FuturnTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task=new FutureTask<String>(new Mycallable());
new Thread(task).start();
if (!task.isDone()){
System.out.println("task has not finished,please wait!");
}
System.out.println("task return:"+task.get());
}
}
结果 :
task has not finished,please wait!
Ready to work
task Done
task return:test
通过线程池:
public class Threadpooldemo {
public static void main(String[] args) {
//创建线程池
ExecutorService newCachedThreadPool= Executors.newCachedThreadPool();
Future<String> future = newCachedThreadPool.submit(new Mycallable());
if (!future.isDone()){
System.out.println("task has not finished,please wait!");
}
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally {
newCachedThreadPool.shutdown();
}
}
}
5 线程的状态
六个状态:
1. 新建(New) :创建后尚未启动的线程的状态。(未调用start()方法)
2. 运行(Runnable):包含Running 和Ready 。(该状态的线程有可能正在执行也可能在等待CPU为其分配时间)
3. 无限期等待(Waiting):不会被分配CPU执行时间,需要被显示唤醒。
4. 限期等待(Timed Waiting):在一定时间后会由系统自动唤醒。
5. 阻塞(Blocked):等待获取排它锁。
6. 结束(Terminated):已终止线程的状态,线程已经结束执行。
6 sleep 和wait的区别
6.1 基本差别
1 sleep 是Thread类的方法,wait是object类中定义的方法。
2 sleep()方法可以再任何地方使用。
3 wait() 方法只能在synchronized方法或者synchronized块中使用。
6.2 最主要的本质区别
1 Thread.sleep 只会让出CPU,不会导致锁行为的改变。
2 Object.wait 不仅让出CPU,还会释放已经占有的同步资源锁。
public class WaITSleepDemo {
public static void main(String[] args) {
final Object lock=new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread A is waiting to get lock");
synchronized (lock){
try{
System.out.println("thread A get lock");
Thread.sleep(20);
System.out.println("Thread A do wait method");
lock.wait(1000);
System.out.println("Thread A is done");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}).start();
try{
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread B is waiting to get lock");
synchronized (lock){
try{
System.out.println("thread B get lock");
System.out.println("Thread B is sleep 10ms");
Thread.sleep(10);
System.out.println("Thread B is done");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}).start();
}
}
结果如下:
Thread A is waiting to get lock
thread A get lock
Thread B is waiting to get lock
Thread A do wait method
thread B get lock
Thread B is sleep 10ms
Thread B is done
Thread A is done
7 notify 和notifyAll的 区别
7.1 两个概念:
锁池EntryList:
假设线程A已经拥有了某个对象(不是类)的锁,而其他线程 B、C想调用这个对象的某个synchronized方法或者(块),由于B、C线程在进入对象的synchronized方法(或者块)之前必须先要获得该对象锁的拥有权,而恰巧该对象的锁目前被线程A所占用,此时B、C线程就会被阻塞,进入一个地方等待锁的释放,这个地方就便是该对象的锁池。
等待池WaitSet:
假设线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁,同时线程A就进入了该对象的等待池中,进入等待池的线程不回去竞争该对象的锁。
7.2 notify 和notifyAll的 区别
1 notifyAll 会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会。
2 notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会。
8 yield
概念: 当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出CPU实用的暗示,但是线程调度器可能会忽略这个暗示。
public class Yield {
public static void main(String[] args) {
Runnable yieldTask=new Runnable() {
@Override
public void run() {
for (int i = 0; i <=10 ; i++) {
System.out.println(Thread.currentThread().getName()+i);
if (i==5){
Thread.yield();
}
}
}
};
Thread t1 = new Thread(yieldTask, "A");
Thread t2 = new Thread(yieldTask, "B");
t1.start();
t2.start();
}
}
9 如何中断线程
9.1 调用interrupt(),通知线程应该中断了
1、如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptException异常。
2.如果线程处于正常活动状态,那么会将该线程的中断标志设置为true,被设置中断标记的线程将继续正常运行,不受影响。
9.2 需要被调用的线程配合中断
1 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
2 如何线程处于正常活动状态,那么会将该线程的中断标志设置为true,被设置中断标记的线程将继续正常运行,不受影响。