对于Java多线程的常见问题
Posted Cloudstrife
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对于Java多线程的常见问题相关的知识,希望对你有一定的参考价值。
最近在准备工作面试的问题,所以找了很多的资料,和自己整理了相关可能会考到的。每天争取发一篇。
1.多线程
实现方法:
一、继承Thread,重写run方法,调用start即可。
Class Thread1 extends Thread{
Public void run(){
//添加代码
}
}
Public static void main(String[] args){
Thread1 st = new Thread1();
St.start();
}
二、实现runnable接口,重写run方法,调用start。
Class RunThrad implements runnable{
Public void run(){
//添加代码
}
}
RunThread t = new RunThread();
Thread th = new Thread(t);
th.start();
三、实现Callable接口类,并重写call方法,使用FutureTask类来包装Callable实现类的对象,并且使用FutureTask对象来作为Thread对象的target来创建线程。
class Mycallable implements Callable<Integer>{
Public int call(){
//添加代码
}
}
public static void main(String[] args){
Mycallable<Integer> mycall = new Mycallable<Integer>();
FutureTask<Integer> ft = new FutureTask<Integer>(mycall);
Thread th = new Thread(ft);
th.start();
}
实现runnable和callable比继承Thread方法的优势:
1、避免单继承的问题。
2、线程池只能够放入runnable和callable类的线程。
线程主要有五种状态,如下:
接下来介绍sleep,wait,notify,notifyAll,join,yield六种方法
对于wait,notify,notifyAll这三个,属于object类的方法,但必须搭配synchronized同步块来使用,即在synchronized修饰的同步代码块中或者方法中调用wait或者notify/notifyAll:
由于wait,notify/notifyAll是放在同步代码块中的,所以线程在执行的时候,肯定是进入了临界状态的,即该线程肯定是获得了锁的。
当执行wait方法时,会把当前的锁释放掉,让出cpu,进入等待状态。
当执行notify/notifyAll方法时,唤醒一个等待该对象锁的线程,然后继续往下执行,直到执行完synchronized的代码后,再将锁释放。(注意,notify/notifyAll执行后,并不立即释放锁,而是需要等待执行完synchronized的代码后)
如果在线程A,线程B中,在线程A中,执行wait,在线程B中执行notify,如果线程B先执行了notify,然后就结束了,然后线程A才去执行wait,那么此时线程A就无法被唤醒了。举例如下:有3个线程A,B,C。
class T implements Runnable{
public String i;
public String index;
public T(String i,String index){
this.i = i;
this.index = index;
}
@Override
public void run() {
synchronized (index) {
while (true) {
try {
index.notifyAll();
System.out.println(i);
index.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class demo {
public static void main(String[] args){
String string1 = "s1";
String string2 = "s2";
String string3 = "s3";
String index = "test";
T a1 = new T(string1,index);
T a2 = new T(string2,index);
T a3 = new T(string3,index);
Thread thread1 = new Thread(a1);
Thread thread2 = new Thread(a2);
Thread thread3 = new Thread(a3);
thread1.start();
thread2.start();
thread3.start();
}
}
输出结果:
s2
s1
s3
s1
s2
s1
s3
s1
……
多线程中测试某个条件的变化用 if 还是用 while?
以前一直不明白 当在线程的run()方法中需要测试某个条件时,为什么用while,而不用if,直到看到了这个简单的例子,终于明白了。。。。
这个例子是这样的:
有两个线程从List中删除数据,而只有一个线程向List中添加数据。初始时,List为空,只有往List中添加了数据之后,才能删除List中的数据。添加数据的线程向List添加完数据后,调用notifyAll(),唤醒了两个删除线程,但是它只添加了一个数据,而现在有两个唤醒的删除线程,这时怎么办??
如果用 if 测试List中的数据的个数,则会出现IndexOutofBoundException,越界异常。原因是,List中只有一个数据,第一个删除线程把数据删除后,第二个线程再去执行删除操作时,删除失败,从而抛出 IndexOutofBoundException。
但是如果用while 测试List中数据的个数,则不会出现越界异常!!!神奇。
当wait等待的条件发生变化时,会造成程序的逻辑混乱---即,List中没有数据了,再还是有线程去执行删除数据的操作。因此,需要用while循环来判断条件的变化,而不是用if。
两个线程之间的通信,可以采用如下两种方式,一种是基于while轮询,另外一种就是wait/notify的方式。
基于while轮询的,B线程一直 一直循环条件,当符合条件时,则抛出异常,结束线程。
基于wait/notify的方式,则使用共享变量。当A线程放弃CPU使用权,进入阻塞状态,则B线程获得共享变量,开始执行,执行完毕后,B线程结束,放弃CPU使用权,A线程继续。容易出现的问题是,A线程尚未执行,B线程先执行,打乱执行顺序。则逻辑错误。
参考案例在:http://www.cnblogs.com/hapjin/p/5492619.html
sleep:
sleep让当前线程休眠指定的时间。休眠完成后,状态转到就绪状态。
yield: yield是放弃当前CPU资源,将CPU资源让给其他线程去使用,但放弃的时间不确定。
join:
大部分情况下,主线程启动了子线程,如果子线程需要完成大量复杂的运算,则主线程会先于子线程结束。但主线程如果需要在子线程运行完毕后使用子线程的结果,则必须在主线程中使用join,这样主线程会等待子线程结束,然后主线程再结束。
方法join,是使得所属线程对象x正常执行完run方法,而使当前线程z进行无限制阻塞,直到对象x结束后,再继续执行z后面的代码。
public class demo
{
public static void main(String[] args) throws InterruptedException
{
Thread producer = new Producer();
producer.start();
producer.join();
System.out.println("main end");
}
}
class Producer extends Thread
{
public void run()
{
for (int i = 0; i < 100000; i++)
System.out.println("producer end");
}
}
在以上案例中,当把producer.join()注释掉后,会先打印main end,再打印producer end。因为子线程运算复杂,所以主线程会先结束。而如果加上这一句,则主线程会等待producer线程的结束,主线程再销毁。也就是说,哪一个线程被调用,则必须等待该线程结束,调用的主线程才能继续执行下一步。
以上是关于对于Java多线程的常见问题的主要内容,如果未能解决你的问题,请参考以下文章