第四章 Java并发编程基础
优先级
操作系统使用时分的形式调度运行的线程。每个线程会被分配到若干时间片,当线程时间片用户线程发生调度,执行下一个线程。当前线程等待下次分配。
通过成员变量priority设置优先级。范围1-10。默认5。数值越大优先级越高,10最高。
线程的状态
Java线程可能处于一下6中状态,在给定的某一时刻只能处于一种状态。
1.实例化后,调用start()之前都处于初始状态。
2.当线程调用start()后,线程进入运行状态
3.如果线程中有对象执行了wait()方法,当前线程进入等待状态。需要notify()方法唤醒
4.如果线程中有对象执行了wait(long)、sleep(long)方法等,进入超时等待状态。需要notify()方法唤醒或者超时时间到。对等待状态设置了超时时间
5.如果调用同步方法,在没有获得锁的情况下会进入阻塞状态。
6.线程执行完进入终止状态。
Java将运行和就绪两个状态合并为运行状态。
阻塞状态是进入synchronized方法/代码块(获取锁)时的状态
Daemon线程(守护线程)
Java可以创建两种线程:用户线程和守护线程。用户线程就是一般创建的线程。守护线程是后台线程,可以使JVM的线程也可以自己创建守护线程。
Daemon线程是一种支持型线程,当线程只剩下守护线程时,JVM就会退出。如果还有其他的用户线程,JVM就不会退出。
Thread.setDaemon(true)来设置为Daemon线程。
注意:要在线程运行之前设置为守护线程,启动之后设置就没用了
注意:守护线程会因为JVM关闭,立即终止。所以守护线程中的finally块不一定会执行。不能用来确保执行close操作和清理资源的逻辑。
Thread初始化
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
。。。。。
Thread parent = currentThread(); //当前线程就是该线程的父线程,有当前线程创建子线程
。。。。
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority(); //使用父线程的daemon和priority
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
//使用静态变量,使用++来获取下一个线程id
tid = nextThreadID();
}
启动线程
Thread通过start()方法启动线程,方法含义:当前线程(pattern线程)同步告知JVM,只要线程规划器空闲,应立即调用start()方法。
调用了 start0()。 private native void start0();
注意:对于自定义的线程,最好可以添加名称,方便排查错误或者分析程序。
volatile和synchronized关键字
支持多个线程同时访问同一个对象或者成员变量。每个线程拥有对象的拷贝,保存在线程内存中。每个线程内存对其他线程不可见。
使用volatile可以用来修饰字段,告知程序线程中对volatile变量的修改,立即更新到主内存中。可以线程中使用的对象是主存中最新的状态。但是并不能解决同步问题(同时获取到同一个值)。
synchronized可以修饰方法或者同步块的形式来使用,确保多个线程在同一时刻,只有一个线程处于同步块或者同步方法中。(对象锁)。确保线程对变量访问的可见性和排他性。
等待和通知(wait/notify)
public class WaitNotify {
public static Object lock = new Object();
public static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(new Wait()).start();
Thread.sleep(2000);
new Thread(new Notify()).start();
}
static class Wait implements Runnable {
@Override
public void run() {
//加锁,拥有lock的Monitor
synchronized (lock) {
while (flag) {
try {
System.out.println(Thread.currentThread() + "flag is true" );
lock.wait(); //释放synchronized (lock) 拥有的锁,释放同步锁
} catch (Exception e) {e.printStackTrace();}
}
}
//Notify线程设置flag为false,结束工作
System.out.println(Thread.currentThread() + "flag is false");
}
}
static class Notify implements Runnable {
@Override
public void run() {
//因为lock.wait()放弃了同步锁,所以Notify线程能获取lock的锁,拥有lock的Monitor
synchronized (lock) {
System.out.println(Thread.currentThread() + " hold lock , notify" );
lock.notify(); //唤醒等待的线程(Wait)
flag = false;
try {Thread.sleep(5000);} catch (InterruptedException e) {}
}
System.out.println(Thread.currentThread() + "flag is false" );
//当Wait线程执行完Synchronized同步块后,进入同步代码块
synchronized (lock) {
System.out.println(Thread.currentThread() + " hold lock again, notify" + System.currentTimeMillis());
}
}
}
}
1.使用wait(),notify(),notifyAll(),需要先对调用对象加锁(可以就是说要放在synchronized代码块中)
2.从wait()方法返回得前提是获得了调用对象的锁(也就是所其他线程调用了notify(),并且其他线程释放锁)
3.调用wait()方法后,线程从RUNNING变为WAITING,放到对象的线程等待队列。
4.其他线程调用notify() 方法后,线程被唤醒,从线程等待池中进入等锁池,从WAITING变为BLOCKED。等待其他线程执行完毕,释放同步锁。
等待/通知的范式
等待方
synchronized (对象) { //1.获取对象锁·
while (条件不满足) { //2.如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件(自旋锁)
对象.wait()
}
对应的处理逻辑 //3.条件满足则执行对应的逻辑
}
通知方
synchronized (对象) { //1.获取对象锁·
2.改变条件
对象.notifyAll() //3.通知所有等待池中的线程
}
管道输入/输出
主要用于线程之间的数据传输。主要有一下4个实现 PipeOutputStream,PipeInputStream和PipeReader,PipeWriter前面两者面向字节流,后面两者面向字符
public class PipeInOut {
public static void main(String[] args) throws IOException {
PipedReader in = new PipedReader();
PipedWriter out = new PipedWriter();
out.connect(in); //对应Pipe类型的流需要 使用connect进行绑定。没有将输入输出进行绑定,对流的的访问会有问题。
new Thread(new Send(out)).start();
new Thread(new Print(in)).start();
}
static class Send implements Runnable{
private PipedWriter out ;
public Send(PipedWriter out) {
super();
this.out = out;
}
@Override
public void run() {
int receive = 0;
try {
while((receive = System.in.read() ) != -1){
out.write(receive);
}
} catch (IOException e) {e.printStackTrace();
} finally{
out.close();
}
}
}
static class Print implements Runnable{
private PipedReader in ;
public Print(PipedReader in) {
super();
this.in = in;
}
@Override
public void run() {
int receive = 0;
try {
while((receive = in.read() ) != -1){
System.out.println((char)receive); //接受到的为int型,需要转化为Char
}
} catch (IOException e) {e.printStackTrace();}
}
}
}
Thread.join()方法
如果当前线程A,执行了thread.join()。当前线程A会等待thread执行完以后再从join()返回。还提供了join( long )的具有超时特性的方法。
join()会使主线程等待,但是本身的线程没有影响。
public class JoinTest {
public static void main(String[] args) {
Thread pervious = Thread.currentThread();
for(int i=0; i<10 ;i++){
Thread thread = new Thread(new Domino(pervious));
thread.start();
pervious = thread;
}
System.out.println(Thread.currentThread().getName() + " terminate");
}
static class Domino implements Runnable{
Thread thread;
public Domino(Thread thread) {
this.thread = thread;
}
public void run() {
try {
thread.join(); //当前线程会被阻塞,thread对象线程 不会被阻塞
} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName() + " terminate");
}
}
}
join主线程等待子线程执行完毕
public static void main(String[] args){
System.out.println("main thread starting...");
List<MyThread> list = new ArrayList<MyThread>();
for (int i = 1; i <= 5; i++) {
MyThread my = new MyThread("Thrad " + i);
my.start();
list.add(my);
}
try{
for (MyThread my : list){
my.join(); //main线程会等待,但是my线程并没有影响
}
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("main thread end...");
}
}
ThreadLocal
public class ThreadLocalTest {
String name;
static ThreadLocal<Integer> sint = new ThreadLocal<Integer>(){
protected Integer initialValue(){
return (int)(Math.random()*10);
}
};
ThreadLocal<Integer> oint = new ThreadLocal<Integer>(){
protected Integer initialValue(){
return (int)(Math.random()*10);
}
};
public static void main(String[] args) throws InterruptedException {
ThreadLocalTest e1 = new ThreadLocalTest("e1");
ThreadLocalTest e2 = new ThreadLocalTest("e2");
new Thread(new MyRunnable(e1,e2)).start();
Thread.sleep(100);
new Thread(new MyRunnable(e1,e2)).start();
}
static class MyRunnable implements Runnable{
打印e1,e2的静态变量和成员变量
}
可以看出来:
在同一线程中,static变量的值是一样的。但在不同线程中,static的值不同。
在同一线程中,成员变量的值是不一样的。在不同线程中就更不一样了。
}