您的位置:首页 > 编程语言 > Java开发

Java并发编程艺术 4 Java并发编程基础

2017-08-29 22:41 288 查看
第四章 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();
[align=left]注意:对于自定义的线程,最好可以添加名称,方便排查错误或者分析程序。[/align]

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的值不同。
在同一线程中,成员变量的值是不一样的。在不同线程中就更不一样了。
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java bingfa 笔记