Java基础二、多线程

35、并行和并发的区别?

并行:一个处理器同时处理多个任务

并发:多个处理器或者多核处理器同时处理多个不同的任务。

36、线程与进程的区别

一个程序至少有一个进程,一个进程下至少有一个线程。一个程序下可以有多个线程来增加程序的执行速度

37、守护线程是什么?

守护线程是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或者等待处理某些发生的事件。在Java中,垃圾回收线程就是特殊的守护线程。

38、创建线程有哪几种方式

1、继承Thread重写run方法

2、实现Runnable接口

3、实现Callable接口

Java创建线程的三种方式

39、说一下runnable和callable有什么区别?

runnable没有返回值

callable有返回值。

Java创建线程的三种方式

40、线程有哪些状态?

线程状态

NEW: 尚未启动

RUNNABLE 正在执行中

BLOCKED 阻塞的(被同步锁或IO锁阻塞)

WAITING 永久等待状态

TIMED_WAITING 等待指定的时间重新被唤醒的状态

TERMINATED 执行完成

41、sleep()和wait()有什么区别? Sleep和Wait的区别

类的不同: sleep()来自Thread, wait() 来自Object   

(本质区别)释放锁:Thrad.sleep只会让出CPU,不会导致锁行为的改变 

                               Object.wait不仅让出CPU,还会释放已经占有的同步锁资源

用法不同: sleep() 时间到了会自动恢复; wait()可以使用notify

                   wait只能在synchronized方法或者synchronized块中使用 (因为wait会释放锁,所有只有获取了锁,才会释放锁)

42、notify() 和 notifyAll()的区别?  notify和notifyAll的区别

notifyAll() 唤醒所有的线程。 notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行。如果不成功则留在锁池里等待释放后再次参与竞争。

notify()只会唤醒一个线程由虚拟机控制。

43、线程run()和start()的区别?

start() 用于启动线程,run()方法执行线程的运行时代码。 run() 方法可以重复调用,而start() 方法只能调用一次。

 调用start方法会创建一个新的子线程并启动

run方法只是Thread的一个普通方法的调用。

查看start方法,可以发现里面调用了一个native方法start0

private native void start0();

如何查询start0方法,可以通过open jdk网址 http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/

Thread.java 位于  java.lang包下。所以在jdk下找到Thread.c

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/b860bcc84d51/src/share/native/java/lang/Thread.c

start0又调用了JVM_Start_Thread方法,这个方法引自jvm.h

 JVM的源码怎么看呢?

进入jvm.cpp 。java用到的原生方法都在里面

http://hg.openjdk.java.net/jdk8u/hs-dev/hotspot/file/ae5624088d86/src/share/vm/prims/jvm.cpp

进入JVM_Start_Thread方法

 通过native_thread = new JavaThread(&thread_entry, sz); 创建java线程。

传入的是thread_entry。 thread_entry首先创建一个线程,然后调用run方法。

44、创建线程池的方式

线程池创建的7中方式

1)newSingleThreadExecutor(), 工作线程限制为1

2) newCacheThreadExecutor(), 处理大量短时间任务的工作任务的线程池。

3)newFixedThreadExecutor() , 重用指定数目的线程
4) newSingleThreadScheduleExecutor() 创建单线程池,可以进行定时或周期性的工作调度
5) newThreadSchedulePool()  和newSingleThreadScheduleExecutor类似,区别是多个线程
6)newWorkStealingPool(int parallelism)
7) ThreadPoolExecutor() 最原始的线程池创建,上面1)-3)都是对ThreadPoolExecutor的封装
 

notify()唤醒一个线程.

46. 线程池中的submit() 和 execute() 方法有什么区别?

execute() 只能执行Runnable类型的任务。

submit() 可执行Runnable和Callbable类型的任务。

Callable类型的任务可以获取执行的返回值, 而Runnable执行无返回值

47 在Java程序中怎么保证多线程的运行安全?

1、使用安全类,比如Java.util.concurrent 下的类

2、使用自动锁synchronized

3、使用手动锁Lock

手动锁Java实例代码如下:

49. 什么是死锁(deadlock)

50、怎样防止死锁

1) 尽量使用tryLock(long timeout, TimeUnit unit)的方法,设置超时时间,超时可以退出防止死锁。

2) 尽量使用Java.unit.concurrent并发类代替手写锁

3) 尽量降低锁的使用粒度,尽量不要几个功能同一把锁

4) 尽量减少同步代码块

51 ThradLocal是什么? 有哪些使用场景?

ThreadLocal为每个使用该变量的线程提供独立的副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程的副本。

ThreadLocal的经典使用场景是数据库和session管理

52.synchronized底层实现原理? synchronized

synchronized是由一堆monitorenter/monitorexit指令实现的, monitor对象是同步的基本实现单元。在Java6之前,monitor的实现完全依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。但是在Java6的时候,Java虚拟机对此进行了大刀阔斧的改进,提供了三种不同的monitor实现,也就是常说的三种不同的锁: 偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了性能。

1) synchronized 关键字: 当synchronized 关键字修饰一个方法的时候,该方法叫做同步方法。

2) Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法访问synchronized 方法了,直到之前的那个线程执行完毕后(或者抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized 方法。

3)如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。

4)如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized 方法所在的对象对应的Class对象。 因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说线程先去执行方法,执行完毕后另一个线程才开始执行。

5)synchronizedkua块,写法:

synchronized(object)

{}

表示线程在执行的时候对object对象上锁。

实例代码:

public class ThreadTest5 {
    
    public static void main(String[] args) {
        Example2 example = new Example2();
        Thread t1 = new TheThread3(example);
        Thread t2 = new TheThread4(example);
        t1.start();
        t2.start();
    }
    
    
}

class Example2{
    
    private Object object = new Object();
    
    public  void execute() {
        
        synchronized (object) {
            for(int i=0; i < 50; i++){
                try {
                    Thread.sleep((long)Math.random() * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("hello:" + i);
            }
        }
        
        
    }
    
    public  void execute2() {
        
        synchronized (object) {
            for(int i=0; i < 50; i++){
                try {
                    Thread.sleep((long)Math.random() * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("world:" + i);
            }
        }
        
        
    }
    
}

class TheThread3 extends Thread{
    
    private  Example2 example;
    

    public TheThread3(Example2 example) {

        this.example = example;
    }


    @Override
    public void run() {
        this.example.execute();
    }
}


class TheThread4 extends Thread{
    
    private  Example2 example;
    

    public TheThread4(Example2 example) {

        this.example = example;
    }


    @Override
    public void run() {
        this.example.execute2();
    }
}
View Code

 6)synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法。synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的。

53、synchronnized和volatile的区别?

54、synchronized和Lock的区别

synchronized可以给类,方法,代码块加锁;而lock只能给代码块加锁。

synchronized不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁; 而lock需要自己加锁和释放锁,如果使用不当没有unlock去释放就好造成死锁。

通过Lock可以知道有没有成功获取锁,而synchronized无法办到。

线程组:

所有的线程都隶属于一个线程组。那可以是一个默认线程组,亦可是一个创建线程时明确指定的组。

说明:

     在创建之初,线程被限制到一个组里,而且不能改变到一个不同的组

    若创建多个线程而不指定一个组,它们就会与创建它的线程属于同一个组。

55、 如何实现处理线程的返回值

56、atomic原理?

atomic主要利用CAS(Compare And Swap)和volatile和native方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升。

参考   atomic的实现原理, atomic包的原理及分析

原文地址:https://www.cnblogs.com/linlf03/p/10477011.html