JAVA多线程:实现方法与面试题总结

java线程的五种状态:

创建,就绪(调用start以后),运行,阻塞(sleep,suspend,wait),死亡(run方法执行完毕或者调用stop方法,无法再进入就绪

java的线程通常由以下2种方法实现:

1. 继承Thread类重写run()方法:

thread本质也是实现了Runnable的接口的一个实例,启动线程的唯一方法就是start()方法。

在这里,start() 方法会启动一个新线程然后执行run() 方法,调用start()以后并不是马上就触发run()方法,而是使该线程变为Runnable的状态

Class MyThread extends Thread{
    public void run(){
        System.out.println("重写run方法”);
    }
}

Public class Test{
    public static void main(String[] args){
        MyThread t = new Mythread();
        t.start();//开启线程
    }
}

2.实现Runnable接口, 实现run()方法:

简单例子

public class Bank {
    private static int money;
    public int getMoney(){
        return money;
    }
    public void saveMoney(int m){
        synchronized (this) {
            System.out.println("存钱后的总金额:"+(money+=m));         
        }
    }
    public void drawMoney(int m){
        synchronized (this) {
            Bank bank = new Bank();
            if (bank.getMoney()<=0) {
                System.out.println("没得钱,取个pi");
            }else {
                System.out.println("取钱后剩的总金额:"+(money-=m));        
            }
        }
    }
     
    public static void main(String[] args) {
        Man m1 = new Man();  //实例化
        Women w = new Women();  //实例化
        Thread t1 = new Thread(m1);   
        Thread t2 = new Thread(m1);
        Thread t3 = new Thread(m1);
        Thread t4 = new Thread(w);
        Thread t5 = new Thread(w);
        Thread t6 = new Thread(w);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
        }
 
}
 
class Man implements Runnable{     //实现runnable接口
    private Bank bank = new Bank();
 
    public void run() {            //实现run()方法
        int m = 100;
        int i=0;
        while (i<5) {
                bank.saveMoney(m);
                i++;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }  
        }
    }
}

class Women implements Runnable{
    private Bank bank = new Bank();
 
    public void run() {
        int m = 100;
        int i=0;
        //bank.getMoney()>0
        while (i<5) {
                    bank.drawMoney(m);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        i++;
        }
    }
     
}

经典面试:

1. 什么是线程, 与进程的区别是什么? 为什么要使用多线程?

线程可以被看做轻量级的进程, 是程序的一个最小执行单元, 一个进程可以拥有多个线程,各个线程共享进程中的内存空间(代码段,数据段,堆空间)。 各个线程也有自己的空间,每个线程互不影响的并发执行。

多线程研发的好处:

  1. 减少程序响应时间,提高效率: 比如某个操作很耗时或者陷入长时间等待(例如网络等待响应)这时候键盘跟鼠标将不会有响应,如果用多线程把这个线程先挂起等待,可以使程序具备更好的交互性。
  2. 线程的创建跟切换开销比较小多个线程共享堆内存(heap memory),因此创建多个线程去执行一些任务会比创建多个进程更好
  3.  多cpu计算机本来就有多线程能力,要利用

2. 怎样实现多线程, 哪个好?

1. 继承Thread类重写run()方法   2.实现Runnable接口并实现run()方法  3. 实现Callable接口,重写call()方法, 会有返回值并允许抛出异常。

实现Runnable接口比较好,因为java只能单一继承, 继承了Thread类就无法继承其他类。

3. 解释一下守护线程, 跟用户线程的区别。

java提供了两种线程: 用户线程跟守护线程

 # 守护线程的存在与否不影响进程, 只要用户线程全部终止,那么进程运行终止

 # 守护线程必须再stat()方法调用之前,调用setDaemon(true)

 # 守护线程用来在后台提供通用服务,优先级较低, 其他的跟用户线程几乎一样。

4. join() 方法的作用是什么?

join()方法使让调用该方法的线程在执行完run()方法后,再执行join()方法后面的代码, 让两个线程合并,用于实现同步功能。

比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。 

thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。 

现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?

代码实现:


public static void main(String[] args) {
        method01();
        method02();
    }
 
    /**
     * 第一种实现方式,顺序写死在线程代码的内部了,有时候不方便
     */
    private static void method01() {
        Thread t1 = new Thread(new Runnable() {
            @Override public void run() {
                System.out.println("t1 is finished");
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override public void run() {
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 is finished");
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override public void run() {
                try {
                    t2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t3 is finished");
            }
        });
 
        t3.start();
        t2.start();
        t1.start();
    }
 
 
    /**
     * 第二种实现方式,线程执行顺序可以在方法中调换
     */
    private static void method02(){
        Runnable runnable = new Runnable() {
            @Override public void run() {
                System.out.println(Thread.currentThread().getName() + "执行完成");
            }
        };
        Thread t1 = new Thread(runnable, "t1");
        Thread t2 = new Thread(runnable, "t2");
        Thread t3 = new Thread(runnable, "t3");
        try {
            t1.start();
            t1.join();
            t2.start();
            t2.join();
            t3.start();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

5. Sleep() 跟wait() 的区别? yield() ?

Sleap() 是Thread 类的静态方法,是线程用来控制自身流程使其暂停一段时间,把执行机会让给其他线程。

wait() 是object类的方法,用于个线程之间的通信。这个方法会使拥有该对象锁的进程等待,直到其他线程调用notify或者notifyAll的时候菜醒来, 开发人员也可也给定时间自动醒来。

 # 调用sleap() 跟wait()都会使当前线程暂停但是sleap 不会释放锁, wait会释放所占用的锁,让其同步的数据被别的线程所使用。Wait通常被用于线程间交互,sleep通常被用于暂停执行。

yield() 方法是让线程重新回到可执行状态,然后给优先级最高的线程以运行机会。 sleep() 必须抛出InterruptedException异常, yield不用

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

- Thread常用方法有:start/stop/yield/sleep/interrupt/join等,他们是线程级别的方法,所以并不会太关心锁的具体逻辑。 
- Object的线程有关方法是:wait/wait(事件参数)/notify/notifyAll,他们是对象的方法,所以使用的时候就有点憋屈了,必须当前线程获取了本对象的锁才能使用,否则会报异常。但他们能更细粒度的控制锁,可以释放锁。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

猜你喜欢

转载自blog.csdn.net/DamonUp/article/details/88137632