Java高并发设计-------------Java并行程序基础------------2

Java并行程序基础

2.1有关线程必须知道的事

进程:系统进行资源分配和调度的基本单位,线程的容器;程序是指令、数据及其组织形式的描述,进程是程序的实体

线程:线程就是轻量级进程,是程序执行的最小单位。

使用多线程而不是多进程去并发程序的设计,是因为线程间的切换和调度的成本远远小于进程!!!!

Idea中全局查找一个类快捷键:ctrl + shift + n

/*
线程状态
*/
public
enum State { NEW, //线程创建
RUNNABLE, //线程执行
BLOCKED, //如果线程在执行过程中遇到了synchronized同步块,就会进入到阻塞状态
TIMED_WAITING, //有时间限制的等待
     WAITING, //无时间限制的等待
        TERMINATED; //执行完成
    }

2.2初始线程:线程的基本操作

两种方法:

  继承Thread类

  

public class ThreadInitDemo {

    public static void main(String[] args) {

        class ThreadDemo extends Thread{
            @Override
            public void run(){
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "--------->" + i);
                }
            }
        }
        ThreadDemo thread = new ThreadDemo();
        thread.start();
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--------->" + i);
        }
    }
}

  实现Runnable接口 

public class ThreadRunnable implements Runnable {
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--------->" + i);
        }
    }

    public static void main(String[] args) {
        new Thread(new ThreadRunnable()).start();
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--------->" + i);
        }
    }
}

 经典皮以下

假如直接调用run方法会怎样?

public class ThreadInitDemo {

    public static void main(String[] args) {

        class ThreadDemo extends Thread{
            @Override
            public void run(){
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "--------->" + i);
                }
            }
        }
        ThreadDemo thread = new ThreadDemo();

    /*
    阿喆请注意!!!!!
    */ thread.run();

for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--------->" + i); } } }

 结果表明:

  并没有开启新的线程,还是主线程执行的!

那么,看看start方法的源码吧:

 1 public synchronized void start() {
 2         /*标志该线程是否启动过,没有启动过才能执行接下来的动作*/
 3         if (threadStatus != 0)  
 4             throw new IllegalThreadStateException();
 5         group.add(this); //将该线程加入到线程组中
 6         boolean started = false;
 7         try {
 8             start0(); //启动start0方法
 9             started = true;  //设置是否启动为true,是否与ThreadStatus重合,此处不懂
10         } finally {
11             try {
12                 if (!started) {
13                     group.threadStartFailed(this);
14                 }
15             } catch (Throwable ignore) {
16             }
17         }
18     }

其中: 

threadStatus:表示这个线程未被启动

group.add(this):将我们创建的线程加入到线程组中,可见线程组是用数组存储的!!

 start0():是一个本地方法,涉及到底层。Java无法直接控制操作系统开辟一条线程,据说Start0()是用c++写的,存储在本地方法库中。c++调用操作系统开辟一条线程,执行我们的任务。

 线程执行步骤:Thread.start():调用start0()方法,创建一条线程,执行run方法;所以我们直接调用run方法,并没有开辟新的线程。

谨记!!!!!!!:只能单继承,所以a喆,能不用继承的方式创建线程就不用。

2.2.2终止线程

Thread.stop():被舍弃了哦,说它太暴力了,强行终止,可以看到stop0(),估计直接调用底层,直接杀死咯;这样可能引起一些数据不一致问题。

  因为:Thread.stop()方法在结束线程,会直接释放所有的锁,这样可能导致数据不一致。

Stop测试代码如下

代码含义:read线程一直在读;write线程一直在写,但是写了150ms之后就终止了;导致id和name不一致!

public class StopThreadUnsafe {

    public static User u = new User();
    public static class User{
        private int id;
        private String name;

        public User() {
            this.id = 0;
            this.name = "0";
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }

    public static class ChangeObjectThread extends Thread{
        @Override
        public void run(){
            while (true){
                synchronized (u){
                    int v = (int)(System.currentTimeMillis()/1000);
                    u.setId(v);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    u.setName(String.valueOf(v));
                }
                Thread.yield();
            }
        }
    }

    public static class ReadObjectThread extends Thread{
        @Override
        public void run(){
            while (true){
                synchronized (u){
                    if (u.getId() != Integer.parseInt(u.getName())){
                        System.out.println(u.toString());
                    }
                }
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new ReadObjectThread().start();
        while (true){
            Thread t = new ChangeObjectThread();
            t.start();
            Thread.sleep(150);
            t.stop();
        }
    }
}

 将上述代码修改正确:

  修改上增加setStopMe()方法,看不懂自己敲一遍就懂咯,就是设置了一个标志位,当写线程stop后,标志为改变,因为写是死循环,所以会检测到标志位改变了,直接退出,并没有时间去修改u。

说句实话,修改main方法中的sleep长短,就还是不安全的。书上会个锤子,害。

但是标志位,是一个保证线程安全的一种方法

 public static class ChangeObjectThread extends Thread{
        volatile boolean stopMe = false;

        public void setStopMe(){
            stopMe = true;
        }
        @Override
        public void run(){
            while (true){
                if (stopMe){
                    System.out.println("我被终结了!!!");
                }
                synchronized (u){
                    int v = (int)(System.currentTimeMillis()/1000);
                    u.setId(v);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    u.setName(String.valueOf(v));
                }
                Thread.yield();
            }
        }
    }
 1 public static void main(String[] args) throws InterruptedException {
 2 
 3         new ReadObjectThread().start();
 4         while (true){
 5             ChangeObjectThread t = new ChangeObjectThread();
 6             t.start();
 7             Thread.sleep(150);
 8             t.stop();
 9             t.setStopMe();
10         }
11     }

2.2.3线程中断

 线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出了!以至于目标线程接收到通知后如何处理,则完全由目标线程自行决定。

public void Thread.interrupt()    // 中断线程    实例方法!!

public boolean Thread.isInterrupted()     // 判断是否中断    实例方法!!!

public static boolean Thread.interrupted()   //判断是否被中断,并清除当前中断状态    类方法!!!
public class InterruptedDemo  {

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(){
          @Override
          public void run(){
              while (true){
                  if (this.isInterrupted()){
                      System.out.println("15");
                      break;
                  }
                  Thread.yield();
              }
          }
        };
        t1.start();
        Thread.sleep(2000);
        t1.interrupt();
    }
}

谨记:调用interrupt()方法后!!!请记得手动的在线程中写中断处理的执行语句

 

 调用Thread.interrupt()方法

判断后,就直接将中断标志清除掉了,不知道有什么用!!!

 

 

sleep函数

 本地方法,会抛出InterruptedException异常

如果睡眠状态的线程被中断了,就抛出异常!InterruptedException不是运行时异常。

运行时异常????(待补充)

2.2.4 等待(wait)和通知(notify)

所有对象都会有的方法

public final void wait() throws InterruptedException;

public final native void notify();

wait

当一个对象实例上调用wait()方法后,当前线程就会在这个对象上等待。

  例如:在线程A上,调用obj.wait()方法,那么线程A就会停止继续执行,转为等待状态。等到,线程A会一直等到其它线程调用obj.notify()方法位置。这时,object对象俨然成了多个线程之间的通信手段

 谨记:wait和notify的方法只能放在同步代码块中使用

 

 

 执行流程:

  • 线程A执行到,object.wait()等待
  • 线程A释放锁
  • 线程B执行,唤醒object,然后执行完毕,释放锁
  • 线程A获得object,执行结束

Thread.sleep()方法可是不会释放锁的哦

2.2.5挂起(suspend)和继续执行(resume)过程

2.2.6等待线程结束(join)和谦让(yeild)

join(有点不太懂的)

public final void join() throws InterruptedException  //没有时间限制

public final synchronized void join(long millis) throws InterruptedException   //有时间限制(一定时候就直接释放了)

例子:join()

public class JoinDemo1 {

    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "----------------->" + i);
            }
        }, "thread");
        t.start();
        t.join();
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "----------------->" + i);
        }
    }
}

 

 

 从上面的例子看出,只有当thread中的任务执行完毕,才能执行thread.join之后的代码

例子证实下:

public class JoinDemo1 {

    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "----------------->" + i);
            }
        }, "thread");
        t.start();
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "----------------->" + i);
        }
        t.join();
        System.out.println("我好了");
    }
}

 

 

 

 猜想正确!!!别整那些将线程加入到哪里,就是摁等,结束了才能执行接下来的线程

那两个线程都join呢?

public class JoinDemo1 {

    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "----------------->" + i);
            }
        }, "thread");
        t.start();
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "----------------->" + i);
        }
        Thread.currentThread().join();
        t.join();
        System.out.println("我好了");
    }
}

上述代码会无限死循环!!!贴join函数源码

 

 join函数:让主线程等待子线程完成才能执行,那么这里是主线程等待主线程就,所以没有意义!

并且,源码显示这还是个死循环。

public class JoinDemo1 {

    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "----------------->" + i);
            }
        }, "thread");
        t.start();
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "----------------->" + i);
            }
        }, "thread2");
        t2.start();
        t.join();
        System.out.println("我好了");
    }
}

 

 当join的线程结束之后才能执行其后面的代码

yield

public static native void yield()  //主动让出当前CPU

我已经完成了一些重要工作,我可以休息一下了,你们来工作吧。

 2.3volatile与Java内存模型(JMM)

volatile:所有线程都可以看到被volatile修饰的变量作的任何改变

  对原子性有很大的帮助,但是对复合性操作的原子性没有帮助

2.4线程组

ThreadGroup:是一个类

创建线程的时候,可以选择加入到线程组当中

new Thread(ThreadGroup, new ThreadGroupName, "T1")

2.5守护线程(Daemon)

垃圾回收线程、JIT线程、、、===========》守护线程

我们的、main线程===========》用户线程,可以将任意一个线程设置为守护线程

当系统中只有守护线程时,就自动退出了,即便此时自定义的线程还在工作,也会退出的。

2.6线程的优先级

public final static int MIN_PRIORITY = 1;

public final static int NORM_PRIORITY = 5;

public final static int MAX_PRIORITY = 10;

优先级的范围1-10之间

thread.setPriority(优先级)

2.7线程安全的概念与关键字synchronized

volatile不能保证线程安全

synchronized锁:

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

  直接作用于实例方法:相当于当前实例加锁

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

 

posted @ 2020-06-02 22:52  梦想成为DALAO  阅读(187)  评论(0编辑  收藏  举报