分享

干货分享|Java多线程面试知识点汇总(超详细总结)

 xkl135 2018-08-21

作者:小柒木有熙

出处:https://blog.csdn.net/hp_yangpeng/article/details/79406099


一、sleep()方法、wait()方法、yeild()方法、interrupt()方法、notify()、notifyAll()方法 


1、sleep()方法: 
sleep方法为Thread的静态方法; 
sleep方法的作用是让线程休眠指定时间,在时间到达时自动恢复线程的执行; 
sleep方法不会释放线程锁;


2、wait()方法: 
wait方法是Object的方法; 
任意一个对象都可以调用wait方法,调用wait方法会将调用者的线程挂起,使该线程进入一个叫waitSet 的等待区域,直到其他线程调用同一个对象的notify方法才会重新激活调用者; 


当wait方法被调用时,它会释放它所占用的锁标记,从而使线程所在对象中的synchronize数据可以被别的线程所使用,

 
所以wait()方法必须在同步块中使用,notify()和notifyAll()方法都会对对象的“锁标记”进行修改,所以都需要在同步块中进行调用, 如果不在同步块中调用,虽然可以编辑通过,但是运行时会报IllegalMonitorStateException(非法的监控状态异常);


3、yeild()方法: 
yeild()方法表示停止当前线程,使该线程进入可执行状态,让同等优先级的线程运行,如果没有同等优先级的线程,那么yeild()方法不 会起作用; 


4、notify()和nofityAll()方法; 
notify()会通知一个处在wait()状态的线程;如果有多个线程处在wait状态,他会随机唤醒其中一个; 
notifyAll()会通知过所有处在wait()状态的线程,具体执行哪一个线程,根据优先级而定; 


java线程的状态 

java线程有四种状态:产生、就绪、执行、阻塞、死亡 


产生:线程被创建,但是未启动(未调用start()) 
就绪:线程被启动(调用start()),处于可执行状态,等待CPU的调度; 
执行:线程正常的运行状态; 
死亡:一个线程正常执行结束; 


阻塞: 
(1)等待阻塞:运行的线程执行wait()方法,jvm会把该线程放入waitSet线程池(释放锁); 
(2)同步阻塞(死锁):运行的线程在获取其他对象的同步锁是,该对象的同步锁被别的线程锁占用,则jvm会将该线程放入线程池中; 
(3)其他阻塞:运行的线程执行sleep方法或者执行t.join()方法,被别的线程打断,jvm把该线程置为阻塞状态,当sleep超时或者join线程结束时线程重新进入就绪状态 


java实现多线程的方法

java实现多线程的方法:继承Thread类,实现runnable接口,实现Callable接口 


1、继承Thread类:

public class MyThread extends Thread {   public void run() {    System.out.println('MyThread.run()');   } } MyThread myThread1 = new MyThread(); myThread1.start();


2、实现Runnable接口:

public class MyThread immplements Runnable { public void run(){ sysout.out.println('MyThread.run()'); } } MyThread runThread = new MyThread(); Thread runThread = new Thread(runThread,''); runThread.start();


3、实现Callable接口,通过FutureTask包装器创建Thread线程

优缺点: 
1)继承Thread类为单继承,实现Runnable方法为多实现,所以在灵活性上来说,使用实现Runnable方法更灵活; 
2)通过实现Runnable接口的方式可以实现多线程内的资源共享; 
3)增加代码的健壮性,代码可以被多个线程共享,代码和数据独立; 
4)线程池只能放实现Runnable或callable类的线程,不能直接放入继承Thread类的线程;


线程调度

1.调整现场优先级:Java线程有优先级,优先级高的线程获得较多的运行机会(运行时间); 
static int Max_priority 线程可以具有的最高优先级,值为10; 
static int MIN_PRIORIYT 线程可以具有的最低优先级,值为1; 
static int NORM_PRIORITY 分配给线程的默认优先级,值为5; 
Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级; 


2.线程睡眠:Thread.sleep(long millins)使线程转到阻塞状态; 


3.线程等待:Object.wait()方法,释放线程锁,使线程进入等待状态,直到被其他线程唤醒(notify()和notifyAll()); 


4.线程让步:Thread.yeild()方法暂停当前正在执行的线程,使其进入等待执行状态,把执行机会让给相同优先级或更高优先级的线程,如果没有较高优先级或相同优先级的线程,该线程会继续执行; 


5.线程加入:join()方法,在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,知道另一个进程运行结束,当前线程再有阻塞状态转为就绪状态;


线程类的一些常用方法

1)sleep():强迫一个线程睡眠N毫秒; 
2)isAlive():判断一个线程是否存活; 
3)join():线程插队; 
4)activeCount():程序中活跃的线程数; 
5)enumerate():枚举程序中的线程; 
6)currentThread():得到当前线程; 
7)isDeamon():一个线程是否为守护线程; 
8)setName():为线程设置一个名字; 
9)wait():线程等待; 
10)notify():唤醒一个线程; 
11)setPriority():设置一个线程的优先级;


线程同步 

线程同步主要使用synchronized关键字;具体有两种实现方式:1、作为关键字修饰类中一个方法;2、修饰方法中的一块区域(代码); 
1、把synchronized当做方法(函数)的修饰符:

public class Name{//类名为Name //getName方法 public synchronized void getName(){ system.out.println(123) }
}

类Name 有两个实例对象n1和n2,此时有两个线程t1和t2;n1在线程t1中执行getName()方法(获得这个方法的锁),那么n1就不能在线程t2中执行getName()方法了,但是n2可以在t1线程中执行getName()方法,同理n2 不能同时在t2线程中执行getName()方法;


所以说实际上synchronized锁的是getName()这个方法的对象(n1和n2),而不是锁的这个方法,这个需要理解; 


2、同步块,实例代码如下:

public void getNameObject o{ synchronized(o){ //TODO } }

这里表示锁住这个变量o; 
这里做一个线程安全的单例模式

public class Car{ //构造方法私有化 private Car(); //创建一个静态的私有的空的常量car private static Car car = null; //对外开放一个静态的共有的方法用来获取实例 public static getInstance(){ if(car == null){ synchronized(Car.getClass()){ if(car == null){ car = new Car(); } } } return car; }
}


线程数据传递 

在传统的开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果,但是在多线程的异步开发模式下,数据的传递和返回同同步开发模式有很大区别。


由于线程的运行和结果是不可预料的,因此在传递和返回数据时就无法像函数一样通过函数参数和return语句来返回数据; 


1、通过构造方法传递参数

package mythread;
public class MyThread1 extends Thread
{
private String name;
public MyThread1(String name)
{
this.name = name;
}
public void run()
{
System.out.println('hello ' name);
}
public static void main(String[] args)
{
Thread thread = new MyThread1('world');
thread.start();
}
}


由于这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了,这样就不会造成数据在线程运行后才传入的现象。


如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。


由于Java没有默认参数,要想实现类似默认参数的效果,就得使用重载,这样不但使构造方法本身过于复杂,又会使构造方法在数量上大增。因此,要想避免这种情况,就得通过类方法或类变量来传递数据。

 

2、通过变量和方法传递数据 
向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。


然后在建立完对象后,通过对象实例逐个赋值。下面的代码是对MyThread1类的改版,使用了一个setName方法来设置 name变量:


package mythread;
public class MyThread2 implements Runnable
{
private String name;
public void setName(String name)
{
this.name = name;
}
public void run()
{
System.out.println('hello ' name);
}
public static void main(String[] args)
{
MyThread2 myThread = new MyThread2();
myThread.setName('world');
Thread thread = new Thread(myThread);
thread.start();
}
}


3、最后是线程之间的数据共享,以下贴出生产者消费者模式的实现: 
实现场景:有一个馒头房,生产者生产馒头,消费者消费馒头,当馒头数量为0时要停止消费,开始生产,当生产的馒头大于5时停止生产,开始消费; 
分析: 
1、馒头类:变量:mt(馒头),num(馒头数量),eatMantou()(吃馒头方法),produceMantou()(生产馒头方法),此处通过构造方法传递对象 


2、消费者类:吃馒头,即用来调用馒头对象的吃馒头方法; 


3、生产者类:生产馒头,即用来调用馒头对象的生产馒头方法; 


4、Test类:用来测试; 
具体代码如下:

package com.yp.producerAndconsumer;/** * @prama 生产者消费者问题中的产品 */public class Mantou { private String mt; private int num; public String getMt() { return mt; } public void setMt(String mt) { this.mt = mt; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } public Mantou(String mt, int num) { this.mt = mt; this.num = num; } /**     * 消费馒头     */ public synchronized void eatMantou(){ //如果馒头数量大于0,则消费馒头 while(num >0){ System.out.println('消费后有:---' this.num '\n'); num--; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } while(num ==0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.notify(); } } /**     * 生产馒头     */ public synchronized void produceMantou(){ while(num<5){ System.out.println('生产后有:---' this.num '个' '\n'); num ; try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } while(num ==5){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.notify(); } }
}


消费者类;

package com.yp.producerAndconsumer;
public class Consumer implements Runnable
{ private Mantou m ; public Consumer(Mantou m) { this.m = m; } @Override public void run() { m.eatMantou(); }
}


生产者类:

package com.yp.producerAndconsumer;

public class Producer implements Runnable
{ Mantou m ; public Producer(Mantou m) { this.m = m; } @Override public void run() { m.produceMantou(); }
}


测试类:

package com.yp.producerAndconsumer;

/**
* @param 生产者消费者问题

* @author YangPeng
*
*/
public class ProducerAndConsumer { public static void main(String[] args) { Mantou mantou = new Mantou('花卷',4); Producer producer = new Producer(mantou); Consumer consumer = new Consumer(mantou); Thread t1 = new Thread(producer); Thread t2 = new Thread(consumer); t1.start(); t2.start(); }
}


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多