随笔 JAVA 基础(一):线程、锁
Post
Cancel

JAVA 基础(一):线程、锁

思考

两个线程在控制台交替打印 1 到 100

这道题看似简单,不过我第一次写不出来,百度了一下,有答案,但不满意,看了下书,果然很有内涵。接下来将循序渐进,先展示创建新线程的方式、再看上面题目的三种解法、再挖掘潜在价值

新线程创建方式

1. Thread

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void test001() throws InterruptedException {
    class ThreadTest extends Thread {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }

    ThreadTest thread = new ThreadTest();
    thread.start();
    
    Thread.sleep(1000);
}

2. Runnable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void test002() throws InterruptedException {
    class Target implements Runnable {

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }

    Target target = new Target();
    Thread thread = new Thread(target);
    thread.start();

    Thread.sleep(1000);
}

3. Callable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void test003() throws InterruptedException {
    class Target<T> implements Callable<T> {

        @Override
        public T call() throws Exception {
            System.out.println(Thread.currentThread().getName());
            return null;
        }
    }

    Target target = new Target();
    FutureTask<Integer> task = new FutureTask<>(target);
    Thread thread = new Thread(task);
    thread.start();

    Thread.sleep(1000);
}

交替打印 1 到 100

具体写法有很多种,不局限于我这几种

1. Thread 写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Test
public void test004() throws InterruptedException {
    class Data {
        Integer a;
    }

    class MyThread extends Thread {

        private Data obj;

        public MyThread(Data obj) {
            this.obj = obj;
        }

        @Override
        public void run() {
            synchronized (obj) {
                while (obj.a < 101) {
                    System.out.println(Thread.currentThread().getName() + ": " + obj.a++);
                    obj.notify();
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    Data obj = new Data();
    obj.a = 1;

    Thread thread001 = new MyThread(obj);
    Thread thread002 = new MyThread(obj);

    thread001.start();
    thread002.start();

    Thread.sleep(1000);
}

2. Runnable 写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Test
public void test005() throws InterruptedException {
    class Target implements Runnable {

        int a = 1;

        @Override
        public synchronized void run() {
            while (a < 101) {
                System.out.println(Thread.currentThread().getName() + ": " + a++);
                notify();
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    Target target = new Target();

    Thread thread001 = new Thread(target);
    Thread thread002 = new Thread(target);

    thread001.start();
    thread002.start();

    Thread.sleep(1000);
}

3. Callable 写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Test
public void test006() throws InterruptedException {
    class Task implements Callable<Integer> {

        int a = 1;

        @Override
        public synchronized Integer call() throws Exception {
            while (a < 101) {
                System.out.println(Thread.currentThread().getName() + ": " + a++);
                notify();
                wait();
            }
            return a;
        }
    }

    Task task = new Task();
    FutureTask<Integer> task001 = new FutureTask<>(task);
    FutureTask<Integer> task002 = new FutureTask<>(task);

    Thread thread001 = new Thread(task001);
    Thread thread002 = new Thread(task002);

    thread001.start();
    thread002.start();

    Thread.sleep(1000);
}

分析

三种写法中,很明显能发现一个共同点,都用了 synchronized 关键字,书中称为同步锁,也可称之为锁。

对于 synchronized,我的理解:多个线程抢夺一个目标,谁抢到谁就可以执行,其它线程就要重新等待目标的释放,再次抢夺

synchronized 可以指定同步监视器,也就是指定抢夺的目标,例如 synchronized (this){...},这个目标只需要有一个特征:所有线程都可以访问到。在 Java 中,堆内存共享,所以同步监视器 = 堆内存中的某一个对象,线程同步就是,你能抢到这个对象,你就可以执行 synchronized 包含的代码,抢不到,就等下一次。

  1. Thread 写法:在堆中定一个了 obj 对象,然后 thread001 和 thread002 就抢夺 obj,抢到的就有执行的资格
  2. Runnable 写法:target 对象为目标
  3. Callable 写法:task 对象为目标

目标在所有线程中是共享的,那么这个目标是不是可以用来线程通信?是的,可以用来通信,wait() 和 notify() 就是通过目标来进行的通信

扩展

实现阻塞队列

https://tech.meituan.com/2018/11/15/java-lock.html

转载请注明出处。