创作赢红包进线程有关的面试题

Posted 冷兮雪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创作赢红包进线程有关的面试题相关的知识,希望对你有一定的参考价值。

一,简述 wait 和 sleep 有什么区别?

  1. sleep是线程类(Thread)的方法;wait是Object类的方法
  2. sleep是使线程休眠,不会释放对象锁;wait是使线程等待,释放锁
  3. sleep()方法导致了程序暂停(线程进入睡眠状态),但是他的监控状态依然保持着,当指定的时间到了又会自动恢复到 可运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
  4. wait是让自己暂时等待,放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
  5. 调用sleep进入阻塞状态;调用wait进入就绪状态

二,请描述 volatile 关键字的作用

  1. 线程的可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
  2. 顺序一致性:禁止指令重排序。

三,sleep()方法和wait()方法区别

共同点:都是使线程暂停一段时间的方法。

不同点:
①原理不同-sleep()是属于Thread类中的,而wait()方法,则是属于Object类中的。

②锁处理机制不同-sleep()最主要作用使线程暂停执行一段时间,时间一到自动恢复,不涉及线程通讯,因此,调用sleep()方法并不会释放锁。而当调用wait()方法的时候,线程会释放它所占的锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

③使用区域不同-wait()方法必须放在同步代码块或者同步方法中使用,sleep()可以用在任何地方

四,简述线程池有什么优点?

1、重用存在的线程,减少对象创建销毁的开销。

2、可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞
争,避免堵塞。

3、提供定时执行、定期执行、单线程、并发数控制等功能。

五、非常重要的面试题:谈谈进程和线程之间的区别![高频]―谈到操作系统,进程线程的概念和区别,就是出场频率最高的问题(没有之一)

1.进程包含线程
2.线程比进程更轻量,创建更快,销毁也更快
3.同一个进程的多个线程之间共用同一份内存/文件资源.进程和进程之间,则是独立的内存/文件资源
4.进程是资源分配的基本单位,线程是调度执行的基本单位

六、工作目录:

1.如果是通过命令行来执行程序,此时当前命令所在的目录就是工作目录~~
2.如果你使用IDEA来执行执行Java程序,这个时候你的工作目录是IDEA打开的项目的目录
3.如果你使用的是Tomcat 来运行一个war包,你的工作目录就是Tomcat的 bin目录~

七、程序和进程的本质区别是:

    程序是静态的,进程是动态的。
    “静态”意味着程序是永久存在的,“动态”表现为进程是一个暂时的过程,可以创建也可以撤销。在进程的一个生存周期中,具有运行、就绪和等待3种可能的状态。 程序和进程也是紧密相关的:进程由程序、数据和进程控制快(PCB)3部分组成,显然如果没有程序,也就没有进程。

创作赢红包Java多线程:synchronized锁方法块

synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间。这种情况下可以尝试使用synchronized同步语句块来解决问题。看一下例子:    
 public class ThreadDomain18 
        public void doLongTimeTask() throws Exception 
            for (int i = 0; i < 100; i++) 
                System.out.println(
                        "nosynchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));
            
            System.out.println();
            synchronized (this) 
                for (int i = 0; i < 100; i++) 
                    System.out.println(
                            "synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));
                
            
        
    
public class MyThread18 extends Thread 
        private ThreadDomain18 td;

        public MyThread18(ThreadDomain18 td) 
            this.td = td;
        

        public void run() 
            try 
                td.doLongTimeTask();
             catch (Exception e) 
                e.printStackTrace();
            
        
    
public static void main(String[] args)
    ThreadDomain18 td = new ThreadDomain18();
    MyThread18 mt0 = new MyThread18(td);
    MyThread18 mt1 = new MyThread18(td);
    mt0.start();
    mt1.start();
运行结果,分两部分来看:
synchronized threadName = Thread-1, i = 1 synchronized threadName = Thread-1, i = 2 nosynchronized threadName = Thread-0, i = 95 synchronized threadName = Thread-1, i = 3 nosynchronized threadName = Thread-0, i = 96 synchronized threadName = Thread-1, i = 4 nosynchronized threadName = Thread-0, i = 97 synchronized threadName = Thread-1, i = 5 nosynchronized threadName = Thread-0, i = 98 synchronized threadName = Thread-1, i = 6 nosynchronized threadName = Thread-0, i = 99 synchronized threadName = Thread-1, i = 7 nosynchronized threadName = Thread-0, i = 100 ... synchronized threadName = Thread-1, i = 98 synchronized threadName = Thread-1, i = 99 synchronized threadName = Thread-1, i = 100 synchronized threadName = Thread-0, i = 1 synchronized threadName = Thread-0, i = 2 synchronized threadName = Thread-0, i = 3 ...
这个实验可以得出以下两个结论: 1、 当A线程访问对象的synchronized代码块的时候,B线程依然可以访问对象方法中其余非synchronized块的部分,第一部分的执行结果证明了这一点 2、 当A线程进入对象的synchronized代码块的时候,B线程如果要访问这段synchronized块,那么访问将会被阻塞,第二部分的执行结果证明了这一点 所以,从执行效率的角度考虑,有时候我们未必要把整个方法都加上synchronized,而是可以采取synchronized块的方式,对会引起线程安全问题的那一部分代码进行synchronized就可以了。 两个synchronized块之间具有互斥性 如果线程1访问了一个对象A方法的synchronized块,那么线程B对同一对象B方法的synchronized块的访问将被阻塞,写个例子来证明一下:
 public class ThreadDomain19 
        public void serviceMethodA() 
            synchronized (this) 
                try 
                    System.out.println("A begin time = " + System.currentTimeMillis());
                    Thread.sleep(2000);
                    System.out.println("A end time = " + System.currentTimeMillis());
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        

        public void serviceMethodB() 
            synchronized (this) 
                System.out.println("B begin time = " + System.currentTimeMillis());
                System.out.println("B end time = " + System.currentTimeMillis());
            
        
    
写两个线程分别调用这两个方法:
public class MyThread19_0 extends Thread
    private ThreadDomain19 td;
    public MyThread19_0(ThreadDomain19 td)
        this.td = td;
    
    public void run()
        td.serviceMethodA();
    

public class MyThread19_1 extends Thread
    private ThreadDomain19 td;
    public MyThread19_1(ThreadDomain19 td)
        this.td = td;
    
    public void run()
        td.serviceMethodB();
    
写个main函数:
public static void main(String[] args)
    ThreadDomain19 td = new ThreadDomain19();
    MyThread19_0 mt0 = new MyThread19_0(td);
    MyThread19_1 mt1 = new MyThread19_1(td);
    mt0.start();
    mt1.start();
看一下运行结果:
A begin time = 1443843271982 A end time = 1443843273983 B begin time = 1443843273983 B end time = 1443843273983
看到对于serviceMethodB()方法synchronized块的访问必须等到对于serviceMethodA()方法synchronized块的访问结束之后。那其实这个例子,我们也可以得出一个结论: synchronized块获得的是一个对象锁,换句话说,synchronized块锁定的是整个对象 synchronized块和synchronized方法 既然上面得到了一个结论 synchronized块获得的是对象锁 ,那么如果线程1访问了一个对象方法A的synchronized块,线程2对于同一对象同步方法B的访问应该是会被阻塞的,因为线程2访问同一对象的同步方法B的时候将会尝试去获取这个对象的对象锁,但这个锁却在线程1这里。写一个例子证明一下这个结论:    
 public class ThreadDomain20 
        public synchronized void otherMethod() 
            System.out.println("----------run--otherMethod");
        

        public void doLongTask() 
            synchronized (this) 
                for (int i = 0; i < 1000; i++) 
                    System.out.println(
                            "synchronized threadName = " + Thread.currentThread().getName() + ", i = " + (i + 1));
                    try 
                        Thread.sleep(5);
                     catch (InterruptedException e) 
                        e.printStackTrace();
                    
                
            
        
    
写两个线程分别调用这两个方法:
public class MyThread20_0 extends Thread
    private ThreadDomain20 td;
    public MyThread20_0(ThreadDomain20 td)
        this.td = td;
    
    public void run()
        td.doLongTask();
    

public class MyThread20_1 extends Thread
    private ThreadDomain20 td;
    public MyThread20_1(ThreadDomain20 td)
        this.td = td;
    
    public void run()
        td.otherMethod();
    
写个main函数调用一下,这里"mt0.start()"后sleep(100)以下是为了确保mt0线程先启动:
public static void main(String[] args) throws Exception
    ThreadDomain20 td = new ThreadDomain20();
    MyThread20_0 mt0 = new MyThread20_0(td);
    MyThread20_1 mt1 = new MyThread20_1(td);
    mt0.start();
    Thread.sleep(100);
    mt1.start();
看一下运行结果:
... synchronized threadName = Thread-0, i = 995 synchronized threadName = Thread-0, i = 996 synchronized threadName = Thread-0, i = 997 synchronized threadName = Thread-0, i = 998 synchronized threadName = Thread-0, i = 999 synchronized threadName = Thread-0, i = 1000 ----------run--otherMethod
证明了我们的结论。为了进一步完善这个结论,把"otherMethod()"方法的synchronized去掉再看一下运行结果:
... synchronized threadName = Thread-0, i = 16 synchronized threadName = Thread-0, i = 17 synchronized threadName = Thread-0, i = 18 synchronized threadName = Thread-0, i = 19 synchronized threadName = Thread-0, i = 20 ----------run--otherMethod synchronized threadName = Thread-0, i = 21 synchronized threadName = Thread-0, i = 22 synchronized threadName = Thread-0, i = 23 ...
"otherMethod()"方法和"doLongTask()"方法中的synchronized块异步执行了 将任意对象作为对象监视器 总结一下前面的内容: 1、synchronized同步方法 (1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态 (2)同一时间只有一个线程可以执行synchronized同步方法中的代码 2、synchronized同步代码块 (1)对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态 (2)同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码 前面都使用synchronized(this)的格式来同步代码块,其实 Java还支持对"任意对象"作为对象监视器来实现同步的功能 。这个"任意对象"大多数是 实例变量 方法的参数 ,使用格式为synchronized(非this对象)。看一下将任意对象作为对象监视器的使用例子:
public class ThreadDomain21 
        private String userNameParam;
        private String passwordParam;
        private String anyString = new String();

        public void setUserNamePassword(String userName, String password) 
            try 
                synchronized (anyString) 
                    System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 进入同步代码块");
                    userNameParam = userName;
                    Thread.sleep(3000);
                    passwordParam = password;
                    System.out.println("线程名称为:" + Thread.currentThread().getName() + "在 " + System.currentTimeMillis() + " 离开同步代码块");
                
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
    
写两个线程分别调用一下:
public class MyThread21_0 extends Thread
    private ThreadDomain21 td;
    public MyThread21_0(ThreadDomain21 td)
        this.td = td;
    
    public void run()
        td.setUserNamePassword("A", "AA");
    

public class MyThread21_1 extends Thread
    private ThreadDomain21 td;
    public MyThread21_1(ThreadDomain21 td)
        this.td = td;
    
    public void run()
        td.setUserNamePassword("B", "B");
    
写一个main函数调用一下:
public static void main(String[] args)
    ThreadDomain21 td = new ThreadDomain21();
    MyThread21_0 mt0 = new MyThread21_0(td);
    MyThread21_1 mt1 = new MyThread21_1(td);
    mt0.start();
    mt1.start();
看一下运行结果:
线程名称为:Thread-0在 1443855101706 进入同步代码块 线程名称为:Thread-0在 1443855104708 离开同步代码块 线程名称为:Thread-1在 1443855104708 进入同步代码块 线程名称为:Thread-1在 1443855107708 离开同步代码块
这个例子证明了: 多个线程持有"对象监视器"为同一个对象的前提下,同一时间只能有一个线程可以执行synchronized(非this对象x)代码块中的代码 锁非this对象具有一定的优点:如果在一个类中有很多synchronized方法,这时虽然能实现同步,但会受到阻塞,从而影响效率。但如果同步代码块锁的是非this对象,则synchronized(非this对象x)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,大大提高了运行效率。 注意一下"private String anyString = new String();"这句话,现在它是一个全局对象,因此监视的是同一个对象。如果移到try里面,那么对象的监视器就不是同一个了,调用的时候自然是异步调用,可以自己试一下。 最后提一点,synchronized(非this对象x),这个对象如果是实例变量的话,指的是对象的引用, 只要对象的引用不变,即使改变了对象的属性,运行结果依然是同步的 细化synchronized(非this对象x)的三个结论 synchronized(非this对象x)格式的写法是将x对象本身作为对象监视器,有三个结论得出: 1、当多个线程同时执行synchronized(x)同步代码块时呈同步效果 2、当其他线程执行x对象中的synchronized同步方法时呈同步效果 3、当其他线程执行x对象方法中的synchronized(this)代码块时也呈同步效果 第一点很明显,第二点和第三点意思类似,无非一个是同步方法,一个是同步代码块罢了,举个例子验证一下第二点:  
public class MyObject 
        public synchronized void speedPrintString() 
            System.out.println("speedPrintString__getLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName());
            System.out.println("----------");
            System.out.println("speedPrintString__releaseLock time = " + System.currentTimeMillis()+ ", run ThreadName = " + Thread.currentThread().getName());
        
    
ThreadDomain24中持有MyObject的引用:
public class ThreadDomain24 
        public void testMethod1(MyObject mo) 
            try 
                synchronized (mo) 
                    System.out.println("testMethod1__getLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName());
                    Thread.sleep(5000);
                    System.out.println("testMethod1__releaseLock time = " + System.currentTimeMillis() + ", run ThreadName = " + Thread.currentThread().getName());
                
             catch (InterruptedException e) 
                e.printStackTrace();
             
        
    
写两个线程分别调用" speedPrintString() "方法和" testMethod1(MyObject mo) "方法:
public class MyThread24_0 extends Thread
    private ThreadDomain24 td;
    private MyObject mo;
    public MyThread24_0(ThreadDomain24 td, MyObject mo)
        this.td = td;
        this.mo = mo;
    
    public void run() 
        td.testMethod1(mo);
    

public class MyThread24_1 extends Thread
    private MyObject mo;
    public MyThread24_1(MyObject mo)
        this.mo = mo;
    
    public void run()
        mo.speedPrintString();
    
写一个main函数启动这两个线程:
public static void main(String[] args)
    ThreadDomain24 td = new ThreadDomain24();
    MyObject mo = new MyObject();
    MyThread24_0 mt0 = new MyThread24_0(td, mo);
    MyThread24_1 mt1 = new MyThread24_1(mo);
    mt0.start();
    mt1.start();
看一下运行结果:
testMethod1__getLock time = 1443855939811, run ThreadName = Thread-0 testMethod1__releaseLock time = 1443855944812, run ThreadName = Thread-0 speedPrintString__getLock time = 1443855944812, run ThreadName = Thread-1 ---------- speedPrintString__releaseLock time = 1443855944812, run ThreadName = Thread-1
看到"speedPrintString()"方法必须等待"testMethod1(MyObject mo)"方法执行完毕才可以执行,没有办法异步执行,证明了第二点的结论。第三点的验证方法类似,就不写代码证明了。

以上是关于创作赢红包进线程有关的面试题的主要内容,如果未能解决你的问题,请参考以下文章

面试46 道阿里巴巴 Java 面试题,你会几道?

27 道阿里巴巴 Java 面试题,你会几道?

46 道阿里巴巴 Java 面试题,你会几道?

阿里最全面试100题:阿里天猫蚂蚁金服阿里巴巴面试题含答案

#面试# 零度Java面试题系列

Java中JVM相关面试题-整理