java并发线程的相关基础知识
1.
Synchronized方法和
Synchronized代码块和
wait(),notify()(
wait,notify方法均属于
Object类方法,
即任何类都具有该方法,至于
Thread类的其他方法都属于其专属方法,需要继承
Thread类,或继承
Runnable接口的,并创建
Thread实例来使用)
(1)
Runnable
接口本身并没有对线程的任何支持,在启动线程时必需创建
Thread
类的实例,比如:
public MyThread implements Runnable(){
int count = 1,number;
public MyThread(int num){
number = num;
System.out.println("创建线程:"+number);
}
public void run(){
while(true){
system.out.println("线程" + number + ":计数" + count);
if(++ count == 6){
return;
}
}
}
public static void main(String[] args){
for(int i = 0;i < 5;i ++){
new Thread(new MyThread(i+1)).start();
}
}
}
(2)继承
Thread
类,作为子类的窗机线程的方式则不需要单独创建
Thread
的实例,而是创建子类的实例:
Runnable接口本身并没有对线程的任何支持,在启动线程时必需创建
Thread类的实例,比如:
public MyThread extends Thread(){
int count = 1,number;
public MyThread(int num){
number = num;
System.out.println("创建线程:"+number);
}
public void run(){
while(true){
system.out.println("线程" + number + ":计数" + count);
if(++ count == 6){
return;
}
}
}
public static void main(String[] args){
for(int i = 0;i < 5;i ++){
new MyThread(i+1).start();
}
}
}
2.每个对象都有锁,每个类也都有锁。
多线程的核心:多个代码块的并发执行。
多线程的特点:各个代码块之间的代码是乱序执行的。
运行程序同时有以上核心及特点的需求时,则需要考虑使用多线程。
3.线程组的相关概念:
(1)线程组是一个Java特有的概念。
(2)在Java中线程组是类
ThreadGroup的对象,每个线程都隶属于唯一一个线程组,
这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。
你可以通过调用包含
ThreadGroup类型参数的
Thread类构造函数(
Thread(ThreadGroup group, Runnable target) ||
Thread(ThreadGroup group, Runnable target, String name) ||
Thread(ThreadGroup group, Runnable target, String name, long stackSize) ||
Thread(ThreadGroup group, String name))
来指定线程属的线程组,若没有指定,则线程缺省地隶属于名为
system 的系统线程组。
(3)在 Java 中,除了预建的系统线程组外,所有线程组都必须显式创建。
(4)在 Java 中,除系统线程组外的每个线程组又隶属于另一个线程组,你可以在创建线程组时指定其所隶属的线程组,若没有指定,则缺省地隶属于系统线程组。
这样,所有线程组组成了一棵以系统线程组为根的树。
(5)Java允许我们对一个线程组中的所有线程同时进行操作,比如我们可以通过调用线程组的相应方法来设置其中所有线程的优先级,
也可以启动或阻塞其中的所有线程。
(6)Java 的线程组机制的另一个重要作用是线程安全。线程组机制允许我们通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处理,
还可以通过线程组的分层结构来支持不对等安全措施的采用。
4.线程中无论是继承
Thread类重写的
run()还是实现
Runnable接口重写的
run()都被称为线程体(
Thread Body)。
5.线程的状态?
NEW:一个刚被创建而未启动的线程处于该状态。由于一个线程实例只能够被启动一次,一次一个线程只可能有一次处于该状态。
RUNNABLE
:这是一个复合状态,它包括两个子状态:
READY
和
RUNNING
状态,前者表示处于该状态的线程可以被JVM的
线程调度器(
Scheduler
)进行调度是指处于
RUNNING
状态,
后者表示处于该状态的线程正在运行,当Thread
实例的
yield()
方法被调用时或者因为线程调度器的原因,相应线程的状态由
RUNNING
转换成
READY
。
BLOCKED:一个线程发起一个阻塞式I/O操作后,或者试图去获得一个由其他线程持有的锁时,相应线程会处于该状态
处于改状态的线程并不会占用CPU资源,当相应的I/O操作完成后,
或者相应的锁被其它线程释放后,该线程的状态可以转换为
RUNNABLE。
WAITING: 一个线程执行了某些方法调用之后就会处于这种无限等待其他线程执行特定操作的状态。
wait() notify() 释放锁
TIMED_WAITING
:该状态处于线程并非无限等待其他线程执行特定操作,而是处于带有时间限制的等待状态。
sleep()
不是释放锁
当其他线程没有在指定时间内执行该线程所期望的特定操作时,该线程的状态自动转换为
RUNNABLE。
TERMINATED: 已经执行结束的线程处于该状态。由于一个线程实例一次只能够被启动一次,因此一个线程一次也只可能有一次处于
该状态。
Thread实例的
run方法正常返回或抛异常而提前终止都会导致相应线程处于该状态。
6.
synchronized的功能不能被继承,即父类同步了某代码块,子类并未同步代码块(重写对应的父类的方法或代码块),
子类的线程非安全,除非实现
synchronized
同步。
7.一个线程就是一个
Thread类实例,创建一个
Thread实例,JVM会为其分配两个调用栈(
Call Stack)
一个用于跟踪java代码间的调用关系,一个用于跟踪java代码对本地代码的调用关系。
一个
Thread实例通常对应两个线程,一个是JVM中的线程(java线程),一个是与JVM中的线程
相对应的依赖于JVM宿主机操作系统的本地(
native
)线程。
8.上下文切换:一个线程的状态由非
Runnable状态进入
Runnable状态时可能涉及恢复之前保存的线程上下文信息
并在此基础上前进。
9.线程的原子性:指相应的操作是单一不可再分的操作,如定义的变量:
int count;当对其进行
count++时就不是原子性操作,实际上分为3步骤
(1)、确认
count的当前值
(2)、使其与
1进行运算
(3)、将运算结果赋予
count
若想实现
count ++ 的原子性操作可以使用
synchronized关键字。
10.线程的内存可见性:线程在CPU内核运行时修改的变量值可能存放在各自CPU的缓冲区内存中,还未存储到主内存中,但修改后的变量值相对于在其他CPU上
运行的线程是不可见的,
syschronized关键字可以实现内存的可见性。
11.
volatile关键字相对于
sychornized关键字只能实现内存的可见性,并不能实现代码操作的原子性。当然
volatile关键字与
synchronized关键字在实现内存的可见性
的方式也不同,
synchronized
是保证一个线程在执行临界区代码所修改的变量值在其他线程执行该临界区的代码时是可见的。
volatile
关键字时将一个线程修改的变量值
每次都保存到主内存中(
RAM
),
而其他CPU中缓存中该变量值会自动失效,而读取该变量在主内存保存的最新值。
12.
volatile
关键字的另一个重要功能则是禁止指令重排序,使的命令的执行数序是以我们为你所期望的顺序执行。
13.当且仅当满足以下所有条件时,才应该使用volatile变量;
(1)对变量的写入操作不依赖变量的当前值,或者可以确保只有单个线程更新变量的值;
(2)该变量不会与其他状态变量一起纳入不变性条件中。
(3)当访问变量时不需要加锁。
未完待续.......................