Java并发编程(一)

Java并发编程

进程与线程

进程

  • 程序由指令和数据构成,但这些指令要运行,数据要读写,就必须将指令加载在CPU,数据加载进内存,在指令运行过程中还需要用到磁盘、网络等设备,进程就是用来加载指令、管理内存、管理IO的
  • 当一个程序被运行,从磁盘加载带这个程序的代码到内存,此时就开启了一个进程
  • 进程就可以认为是程序的一个实例。

线程

  • 一个进程可以分为一到多个线程
  • 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行
  • Java中线程作为最小的调度单位,进程最为资源分配的最小单位,在Windows中进程是不活动的,只是作为线程的容器

进程与线程的对比

  • 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集
  • 进程拥有共享的资源,如内存空间等,供其内部的线程共享
  • 进程间通信较为复杂
    • 同一台计算机的进程通信称为 IPC(Inter-process communication)
    • 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP
  • 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
  • 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低

并行与并发

  • 并发(concurrent)是同一时间应对(dealing with)多件事情的能力
  • 并行(parallel)是同一时间动手做(doing)多件事情的能力

Java线程

创建和运行线程

1. 直接使用Thread

// 创建一个线程对象
  Thread t = new Thread(){
            @Override
            public void run(){
             //要运行的任务 
                log.debug("running");
            }
        };
        //设置线程的名字
        t.setName("t1");
        // 启动线程
        t.start();

2. 使用Runable配合Thread

  • 这个方式可以使线程和任务分开执行
    • Thread代表线程
    • Runable是可执行的任务
  Runnable runnable = new Runnable() {
            @Override
            public void run() {
                log.debug("running");
            }
        };
        // lambda 表达式写法
        // Runnable runnable1 = () ->{log.debug("running");};
        // 参数1是任务对象,参数2是线程的名字
        Thread thread = new Thread(runnable,"t2");
        thread.start();
  • 与方法1比较
    • 1是将线程和方法合并在了一起,2是把线程和任务分开
    • 使用Runable更容易与线程池等高级API配合
    • 用Runable让任务类脱离了Thread继承体系,更灵活

3. FutureTask配合Thread

  • FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
   FutureTask<Integer> futureTask  = new FutureTask<>(
     new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.debug("running...");
                Thread.sleep(1000);
                return 100;
            }
        });
        Thread thread = new Thread(futureTask,"t3");

        thread.start();
        // 等待task返回
        log.debug("{}",futureTask.get());

start与run

调用Run

 public static void main(String[] args) {
        Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                log.debug(Thread.currentThread().getName());
            }
        };
        t1.run();
        log.debug("完成。。。。。。");
    }
  • 输出,由主线程来运行方法输出
22:03:29.423 [main] DEBUG c.Test09 - main
22:03:29.425 [main] DEBUG c.Test09 - 完成。。。。。。

调用start

  • 将上述的run改成start,输出日志如下,两个输出以不同的线程输出
22:06:42.781 [t1] DEBUG c.Test09 - t1
22:06:42.781 [main] DEBUG c.Test09 - 完成。。。。。。

总结

  • 直接调用 run 是在主线程中执行了 run,没有启动新的线程
  • 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码

sleep与yield

方法名 static 功能说明
sleep(long n) static 让当前执行的线程休眠n毫秒,休眠时让出 cpu的时间片给其它线程
yield() static 提示线程调度器让出当前线程对CPU的使用

sleep

  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
  3. 睡眠结束后的线程未必会立刻得到执行
  4. 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

yield

  1. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
  2. 具体的实现依赖于操作系统的任务调度器

线程优先级的设定

  • 线程对象可以通过setPriority();方法来设定优先级
  • 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
  • 如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用

join方法

join()方法分为有参和无参,

方法名 static 功能说明
join
等待线程运行结束
join(long n)
等待线程运行结束,最多等待 n毫秒

join()

  static int r1 = 0;
    static int r2 = 0;
    public static void main(String[] args) throws InterruptedException {
        test12();
    }
    private static void test12() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            r1 = 10;
        });
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            r2 = 20;
        });
        long start = System.currentTimeMillis();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        long end = System.currentTimeMillis();
        log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
    }
  • 第一个 join:等待 t1 时, t2 并没有停止, 而在运行
  • 第二个 join:1s 后, 执行到此, t2 也运行了 1s, 因此也只需再等待 1s
  • 输出为
22:31:55.281 [main] DEBUG c.Test12 - r1: 10 r2: 20 cost: 2005

join(long n)

等待够时间
static int r1 = 0;
    static int r2 = 0;
    public static void main(String[] args) throws InterruptedException {
        test11();
    }
    public static void test11() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            r1 = 10;
        });
        long start = System.currentTimeMillis();
        t1.start();
        // 线程执行结束会导致 join 结束
        t1.join(1500);
        long end = System.currentTimeMillis();
        log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
    }
  • 输出
22:34:22.580 [main] DEBUG c.Test11 - r1: 10 r2: 0 cost: 1006
等待超时
 static int r1 = 0;
    static int r2 = 0;
    public static void main(String[] args) throws InterruptedException {
        test13();
    }
    public static void test13() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            r1 = 10;
        });
        long start = System.currentTimeMillis();
        t1.start();
        // 线程执行结束会导致 join 结束
        t1.join(1500);
        long end = System.currentTimeMillis();
        log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
    }
  • 输出
22:36:17.886 [main] DEBUG c.Test13 - r1: 0 r2: 0 cost: 1506

interrupt 方法

  • 打断 sleep,wait,join 的线程,这几个方法都会让线程进入阻塞状态
  • 打断 sleep 的线程, 会清空打断状态,以 sleep 为例
 private static void test1() throws InterruptedException {
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        Thread.sleep(500);
        t1.interrupt();
        log.debug(" 打断状态: {}", t1.isInterrupted());
    }
  • 输出
java.lang.InterruptedException: sleep interrupted
 at java.lang.Thread.sleep(Native Method)
 at com.wjl.code01.Test01.lambda$test1$0(Test01.java:18)
 at java.lang.Thread.run(Thread.java:750)
22:39:48.973 [main] DEBUG c.Test01 -  打断状态: false
  • 打断正常运行的线程,不会清空打断状态
 private static void test2() throws InterruptedException {
        Thread t2 = new Thread(()->{
            while(true) {
                Thread current = Thread.currentThread();
                boolean interrupted = current.isInterrupted();
                if(interrupted) {
                    log.debug(" 打断状态: {}", interrupted);
                    break;
                }
            }
        }, "t2");
        t2.start();
        Thread.sleep(500);
        t2.interrupt();
    }
  • 输出
22:42:04.128 [t2] DEBUG c.Test01 -  打断状态: true


原文始发于微信公众号(卫颓废):Java并发编程(一)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/47492.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!