Java多线程学习笔记(一)- 基本概念

一、基本概念

1、并发与并行:

   1)并发:多个任务交替执行。

   2)并行:多个任务同时执行。如果系统只有一个CPU,那真实环境中不可能是真实并行,因为一个CPU一次只能执行一个指令。多核CPU有可能出现并行。
 

2、临界区:
     一种公共资源或共享数据,每一次只能有一个线程使用它。一旦临界区资源被占用,其他线程要想使用这个资源,就必须等待。
 

3、阻塞(Blocking)和 非阻塞(Non-Blocking):

     阻塞:用来形容多线程间的相互影响。一个线程占用了临界区,其他需要使用这个临界区的线程就必须等待,等待会导致线程挂起,这种情况就是阻塞。

     非阻塞:没有一个线程可以防碍其它线程执行。
 

4、死锁(DeadLock)、饥饿(Starvation)和活锁(LiveLock):

    死锁: 各线程彼此阻塞。

    饥饿:某个或多个线程由于某种原因(线程优先级太低,或某个线程一直占用资源)无法获得需要的资源,导致一直无法执行。

   活锁:两个线程相互谦让资源,导致没有一个线程可以同时拿到所有资源而正常执行。


5、并发级别:

    根据控制并发的策略,可以发并发级别分为: 阻塞、无饥饿、无障碍、无锁、无等待。   
 

6、多线程的原子性、可见性、有序性:

    a、 原子性( Atomicity ):指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。

           如: 对于32位系统,Long (64位)型数据的读写不是原子性的,

     b、可见性:当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。

           如: 多核CPU中,CPU1 将变量 t 缓存在Cache或寄存器, 如果CPU2上的某线程修改了t 的值,则CPU1 不会知道,这就是可见性的问题。

           可见性问题有多种情况: 缓存优化,硬件优化,指令重排,编辑器优化。

     c、有序性:指令重排后,代码执行顺序会变化。

 

7、线程中断的三种方法:

1)  Thread.interrupt();  // 中断线程

实例方法,通知目标线程中断,即设置中断标志位。中断标志位表示当前线程已经被中断了。
 

2)  Thread.isInterrupted();  // 判断是否被中断

实例方法,判断当前线程是否有被中断(通过检查中断标志位)。

 

3) Thread.interrupted();  // 判断是否被中断,并清除当前中断状态。

也用来判断当前线程的中断状态,但同时会清除当前线程的中断标识位状态。

 

8、Thread.sleep()方法:

  当线程在 sleep()休眠时,如果被中断,则会抛出InterruptException中断异常

该异常不是运行时异常,程序必须捕获并处理它。
 

9、Object.wait()  方法:

当在一个对象实例上调用 wait()方法后,当前线程就会在这个对象上等待,直到其它线程调用了obj.notify()方法为止。

 obj对象成为多个线程之间的通信手段。

Object.wait()方法必须包含在对应的 synchronized语句中。

执行前需要获得目标对象的监视器 - 用synchronized 语句

wait() 和 sleep()方法的区别:
1)、相同点: 都可以让线程等待,

2)、不同点: wati 可以被唤醒,会释放目标对象的锁,slee()方法不会释放任何资源。

 

10、Object.notify()方法:随机唤醒(不公平)等待的线程。

         Object.notifyAll()方法:唤醒全部等待队列中的线程。

执行前需要获得目标对象的监视器


11、suspend( 挂起)和 resume (继续执行)线程:

这是两个相反的操作,已经不建议使用。

因为: suspend()在挂起线程时,不释放任何的锁资源,直至对应的线程执行了resume()操作。

但如果 resume()操作先与 suspend()执行,则被挂起的线程在很难有机会被继续执行。

严重时它占用的锁不被释放,会导致整个系统工作不正常。

 

12、Join (等待线程结束) 和 yield ( 谦让 )  方法:

 Join 方法有两个:

a、 public final void join(): 无限等待,一直阻塞当前线程,直到目标线程执行完毕。

b、 public final synchronized void join( long millis ):给定一个最大等待时间,如超过
等待时间目标线程还在执行,
当前线程会不再等待而继续往下执行。

join()的核心代码是 wait(0);

注:不要在应用程序中,在Thread对象实例上使用类似 wait()或 notify()等方法,
因为这很可能会影响系统API的工作
或者被系统API所影响(为什么? 没有理解)

 

yield()方法定义: public static native void yield();

静态方法,一旦执行,它会使当前线程让出CPU。但后续还会进行CPU资源的争夺。

如果一个线程不太重要或者优先级低,可以在适当的时候调用 Thread.yield().

 

13、Java内存模型:原子性、有序性、可见性。


14、volatile: 当用该关键词声明变量后,虚拟机会特别小心地处理,保证该变量的可见性。
但 volatile 不能代替锁,也无法保证一些复合操作的原子性。

 

15、线程组:

ThreadGroup tg = new ThreadGroup("TGroup");
Thread t1 = new Thread(tg, new ThreadGroupName(), "T1");
Thread t2 = new Thread(tg, new ThreadGroupName(), "T2");

tg.activeCount()

tg.list();

 

16、守护线程( Daemon),与之对应的是用户线程。

Thread t = new DaemonT();
t.setDaemon(true);  //必须在 start()方法前设置。
t.start();

 

17、线程优先级:

java中,线程优先级从1到10。

高优先级的线程竞争资源时会更有优势,但并不绝对。

 

18、线程安全的概念:

1)、synchronized 的基本用法:

   a、指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。

     如: Object instance = new Object();

             synchronized( instance )

             {
             ...
             }

           

            main()

            {
                Thread t1 = new Thread(instance);

                Thread t2 = new Thread(instance);
                t1.start(); t2.start();
               ....

            }

 

   b、直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。

      如: publicsynchronized void increase()

             {
              ...
             }

             main()

            {
                Thread t1 = new Thread(instance);

                Thread t2 = new Thread(instance);
                t1.start(); t2.start();
               ....

            }

   c、直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。

      如:public staticsynchronized void increase()

             {...}

             main()

            {
                Thread t1 = new Thread(new
StaticSyncInstatnce());

                Thread t2 = new Thread(newStaticSyncInstatnce());
                t1.start(); t2.start();
               ....

            }

 

问题: 这三种用法是锁的粒度范围不同?a和b 有什么区别?

 

 

二、线程池

1、工作方式:

      有一个队列,任务被提交到这个队列中(也可有多个队列)。一定数量的线程会从该队列中取任务,

      然后执行。执行完任务后,线程会从任务队列中取下一个任务并执行,如果没有任务要执行,则等待。

 2、线程池有最小线程数最大线程数,池中有最小数目的线程随时待命,等待任务指派给他们。

      最小线程数也叫核心池大小

      最大线程数是一个限流阀,防止一次执行太多线程。最大线程数的大小取决于负载特性以及底层硬件

     最优线程数还与每个任务的阻塞频率有关。

 

3、线程池Executor框架:

ThreadPoolExecutor

Executors 为线程池工厂,包含如下构造线程池的方法:

public staticExecutorServicenewFixedThreadPool() :

   返回一个固定线程数量的线程池。

 

public staticExecutorService newSingleThreadExecutor:

    返回只有一个线程的线程池。


public static ExecutorService newCachedThreadPool

  返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不固定。
 

public staticScheduledExecutorServicenewSingleThreadScheduledExecutor

   返回一个 ScheduledExecutorService 对象,线程池大小为一。

   ScheduledExecutorService接口在ExecutorService 接口只上扩展了在给定时间执行某任务的功能。
    如在某个固定时间执行某任务的功能,如在某个固定的延迟之后执行,或者周期性的执行某个任务。
 

public staticScheduledExecutorService  newScheduledThreadPool

   该方法也返回一个ScheduledExecutorService对象,但该线程可以指定线程数量。

public staticExecutorService  unconfigurableExecutorService

public static ScheduledExecutorService  unconfigurableScheduledExecutorService

 

20、JDK 并发容器:

CuncurrentHashMap:

CopyOnWriteArrayList:

ConcurrentLinkedQueue:

BlockingQueue:

ConcurrentSkipListMap:

 

三、Java线程调试工具

1、jps - 显示当前系统中所有的java 进程。

2、jstack 打印给定 Java 进程的内部线程及堆栈。

3、javap [不带class后缀的类名]-  反编译java 代码

问题

1、ActomicLong 类?

2、JUC(包括重入锁(Lock)、线程池/连接池(commom-pool)、Timer、Future模式、AQS、CAS等)

3、代理,反射

 

    

 

   

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值