0602创建多线程4种方式

💃 线程的生命周期

  • JDK中用Thread.State类定义了线程的几种状态
  • 要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五****种状态
    1. 新建: 当一个Thread类或其子类的对 象被声明并创建时,新生的线程对象处于新建状态
    2. **就绪:**处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
    3. **运行:**当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功能
    4. **阻塞:**在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
    5. **死亡:**线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结

image-20220724180010373

✋卖票中的线程安全问题

  • 原因:存在多个线程共享一个数据时,如果线程a正在处理数据,此时线程a 因为阻塞或者其他问题没有执行完(such as : Thread.currentThread.sleep() ),线程b也开始处理这个数据,此时数据由于线程a 没有执行完,数据没有改变,就会造成不良后果。这就是线程安全问题
  • 阻塞越久,影响越恶劣。(如下的Thread.currentThread.sleep() 的参数大小,以及sleep的位置)
  • 解决方法:当一个线程在操作数据时,其他的线程不能参与进来,直到线程a操作完。即使a 阻塞,也不影响数据操作
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package pers.dhx_.java0602;

public class SafeTest {
public static void main(String[] args) {
Thread1 t1 = new Thread1();
// Thread1 t2 = new Thread1();
// Thread1 t3 = new Thread1();
// olny need 1 Thread1 Object
Thread tt1 = new Thread(t1);
Thread tt2 = new Thread(t1);
Thread tt3 = new Thread(t1);
tt1.setName("线程1");
tt2.setName("线程2");
tt3.setName("线程3");
tt1.start();
tt2.start();
tt3.start();
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1: " + Thread1.test.cnt_1);
System.out.println("线程2: " + Thread1.test.cnt_2);
System.out.println("线程3: " + Thread1.test.cnt_3);
System.out.print("total:");
System.out.println(Thread1.test.cnt_3 + Thread1.test.cnt_2 + Thread1.test.cnt_1);

}

}


class Thread1 implements Runnable {
@Override
public void run() {
while (total > 0) {
// try {
// Thread.currentThread().sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() + " :票号 " + total--);
if (Thread.currentThread().getName() == "线程1") test.cnt_1++;
else if (Thread.currentThread().getName() == "线程2") test.cnt_2++;
else test.cnt_3++;
// } else break;
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}

static Thread1 test = new Thread1();
public int cnt_1 = 0;
public int cnt_2 = 0;
public int cnt_3 = 0;
public int total = 100;
}

⭐java解决线程安全问题 :涉及锁

🍔同步代码块 ----method1

1
2
3
4
5
6
7
8
9
10
public void run()
{
//code
synchronized()//arguments: 同步监视器
{
//code:需要被同步的代码
}
//code
}

  1. 操作 共享数据 的代码即为需要被同步代码
  2. 共享数据:多个Thread共同操作的的数据
  3. 同步监视器,俗称 ,⭐
    • 任何一个类的对象都可以充当锁
    • 要求多个线程共用同一把锁
    • 💔坏处:操作同步代码时,只有一个线程参与,其他线程等待,相当于是一个线程在工作,效率低

同步机制中的锁

  • **同步锁机制:**在《Thinking in Java》中,是这么说的:对于并发工作,你需要某种方式来防止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁之时,另一个任务就可以锁定并使用它了。 -
  • synchronized****的锁是什么?
    1. 任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
    2. 同步方法的锁:静态方法(类名.class)、非静态方法(this)
    3. 同步代码块:自己指定,很多时候也是指定为this或类名.class

🍅注意:

  • 必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就无法保证共享资源的安全

  • 一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方法共用同一把锁(this),同步代码块(指定需谨慎)

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

package pers.dhx_.java0602;

public class SafeTest {
public static void main(String[] args) {
Thread2 t1 = new Thread2();
// Thread1 t2 = new Thread2();
// Thread1 t3 = new Thread2();
// olny need 1 Thread1 Object
Thread tt1 = new Thread(t1);
Thread tt2 = new Thread(t1);
Thread tt3 = new Thread(t1);
tt1.setName("线程1");
tt2.setName("线程2");
tt3.setName("线程3");
tt1.start();
tt2.start();
tt3.start();
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1: " + Thread1.test.cnt_1);
System.out.println("线程2: " + Thread1.test.cnt_2);
System.out.println("线程3: " + Thread1.test.cnt_3);

System.out.print("total:");
System.out.println(Thread1.test.cnt_3 + Thread1.test.cnt_2 + Thread1.test.cnt_1);

}

}


class Thread1 implements Runnable {
Object o1 = new Object();

@Override
public void run() {
while (true) {
synchronized (o1) {
if (total > 0) {
System.out.println(Thread.currentThread().getName() + " :票号 " + total--);
if (Thread.currentThread().getName() == "线程1") test.cnt_1++;
else if (Thread.currentThread().getName() == "线程2") test.cnt_2++;
else test.cnt_3++;
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

}

static Thread1 test = new Thread1();
public int cnt_1 = 0;
public int cnt_2 = 0;
public int cnt_3 = 0;
public int total = 100;
}

✋同步方法 ----method2

1
2
3
4
5
6
7
8
//格式
public void run(){}
private synchronized void method()
{
/*
code
*/
}

👿线程a执行完后,线程b才能进来

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
42
43
44
45
46
47
48
package pers.dhx_.java0602;

/**
* 使用同步方法解决Runnable接口线程安全问题
*
* @create
*/
public class TsbuMethodTest {
public static void main(String[] args) {
Thread2 t1 = new Thread2();
Thread tt1 = new Thread(t1);
Thread tt2 = new Thread(t1);
Thread tt3 = new Thread(t1);
tt1.setName("线程1");
tt2.setName("线程2");
tt3.setName("线程3");
tt1.start();
tt2.start();
tt3.start();
}
}

class Thread3 implements Runnable {
public static final Object o1 = new Object();

@Override
public void run() {
while (true) {
f();
}
}

private synchronized void f() { //同步监视器为 this
if (total > 0) {
//此时 f() 为静态方法, 默认的同步监视器是Thread4.class
//静态只能调用静态, 非静态可以调用静态 System.out.println(Thread.currentThread().getName() + " :票号 " + total--);

try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}

public int total = 100;
}
🎈总结:(同步方法)
  • 同步方法依旧涉及到同步监视器,只是不需要我们显示声明。

  • 非静态的同步方法,同步监视器是:this

    静态的同步方法,同步监视器是当前类本身 :Thread4.class

❤️线程安全的单例模式

  • 懒汉式线程不安全,修改

⭐法一

  • 效率稍差
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
class Bank { //懒汉式
private Bank() {
}

private static Bank instance = null; //相当于共享数据

// private static Bank getInstance() {
private static synchronized Bank getInstance() {
if (instance == null)
//线程a在此处被阻塞,线程b进来发现 instance ==null 也开始new ,导致 instance 被赋值两次
instance = new Bank();
return instance;
}
}

class Bank { //懒汉式
private Bank() {
}

private static Bank instance = null; //相当于共享数据

// private static Bank getInstance() {
private static Bank getInstance() {
synchronized (Bank.class) {
if (instance == null)
//线程a在此处被阻塞,线程b进来发现 instance ==null 也开始new ,导致 instance 被赋值两次
instance = new Bank();
}
return instance;
}
}

⚔️法二

  • 效率稍高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Bank { //懒汉式
private Bank() {
}

private static Bank instance = null; //相当于共享数据
private static Bank getInstance() {:
if (instance == null) {
synchronized (Bank.class)
{
if (instance == null)
instance = new Bank();
}
}
return instance;
}
}

🌟线程的死锁问题

  • 死锁
  1. 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
  2. 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
  • 解决方法
  1. 专门的算法、原则
  2. 尽量减少同步资源的定义
  3. 尽量避免嵌套同步
  • 死锁

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    package pers.dhx_.java0602;

    /**
    * @author Dhx_
    * @className ThreadTest
    * @description TODO 演示线程的死锁问题
    * @date 2022/6/3 16:10
    */
    public class ThreadTest {
    public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer();
    StringBuffer s2 = new StringBuffer();
    new Thread() {
    @Override
    public void run() {
    synchronized (s1) {
    s1.append("a");
    s2.append("1");
    try {
    Thread.sleep(100);
    } catch (InterruptedException e){
    e.printStackTrace();
    }
    synchronized (s2) {
    s1.append("b");
    s2.append("2");

    System.out.println(s1);
    System.out.println(s2);
    }
    }
    }
    }.start();
    new Thread(new Runnable() {
    @Override
    public void run() {
    synchronized (s2) {
    s1.append("c");
    s2.append("3");
    try {
    Thread.sleep(100);
    } catch (InterruptedException e){
    e.printStackTrace();
    }
    synchronized (s1) {
    s1.append("d");
    s2.append("4");
    System.out.println(s1);
    System.out.println(s2);
    }
    }
    }
    }).start();
    }
    }

⭐Lock (锁)JDK5.0 -------method3

  • 从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同

    步锁对象来实现同步。同步锁使用Lock对象充当。

  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。

  • ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和

    内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package pers.dhx_.java0602;

import java.util.concurrent.locks.ReentrantLock;

/**
* @author Dhx_
* @className LockTest
* @description TODO method3 Lock
* @date 2022/6/3 16:34
* 1.面试题:synchronized 与 lock的异同
* 同:二者都可以解决线程安全问题
* 异:synchronized在执行完同步代码块后,自动解锁(自动释放同步监视器)
* Lock 需要手动的启动同步(lock.lock() ),结束也需要手动实现(lock.unlock() )
*/
public class LockTest {
public static void main(String[] args) {
Window w1 = new Window();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.setName("窗口1 ");
t2.setName("窗口2 ");
t3.setName("窗口3 ");
t1.start();
t2.start();
t3.start();
}
}

class Window implements Runnable {
private int tickets = 100;
//TODO 1.实例化ReenTrnatLock

private ReentrantLock lock = new ReentrantLock(true);

@Override
public void run() {
while (true) {
try {
//TODO 2.调用lock方法
lock.lock(); //保证下面的code时单线程执行
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 票号:" + tickets--);
} else break;
} finally {
//TODO 3.调用解锁方法
lock.unlock();
}
}
}
}


💃 synchronized 与 Lock 的对比

  1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放

  2. Lock只有代码块锁,synchronized有代码块锁和方法锁

  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

🍅优先使用顺序:

Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)

💃练习

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package pers.dhx_.java0602;

/**
* @author Dhx_
* @className AccountTest
* @description TODO
* 银行有一个账户。
* 有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打
* 印账户余额。
* @date 2022/6/3 16:54
* 分析:
* 是否是多线程问题? yes 2个储户
* 是否有共享数据 ? yes 账户余额
* 是否有线程安全问题 ? yes
* 如何解决? 3 methods
*/
public class AccountTest {
public static void main(String[] args) {
Account acct = new Account(0);
Customer c1 = new Customer(acct); //TODO 2个线程共用同一个账户
Customer c2 = new Customer(acct);
c1.setName("路人甲");
c2.setName("路人乙");
c2.start();

c1.start();
try {
Thread.currentThread().sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(acct.getBalance());
}
}

class Customer extends Thread {
private Account acct;

Customer(Account acct) {
this.acct = acct;
}

@Override
public void run() {
for (int i = 0; i < 3; i++) {
acct.deposit(1000);
}
}
}

class Account {
private double balance;

Account(double balance) {
this.balance = balance;
}

public double getBalance() {
return balance;
}
// public synchronized void deposit(double add) { //同步方法
// if (add > 0) {
// try {
// Thread.sleep(500);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// balance += add;
//
// System.out.println(Thread.currentThread().getName() + "存钱成功! 余额为: " + balance);
// }
// }
public void deposit(double add) { //同步方法
synchronized (this) { //只有一个Accout类, 可以用this
if (add > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
balance += add;

System.out.println(Thread.currentThread().getName() + "存钱成功! 余额为: " + balance);
}
}
}

}

⭐注意点:

  • 注意如果使用继承方式 ,new了多个对象,并且使用的是Lock类,那么记得要把lock 设为 static

🗡️线程的通信

🎅例 题

  • 使用两个线程打印 1-100。线程1,线程2 交替打印

👡wait() notify() notifyAll()

  • 🌟不理解就看代码注释

  • wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。

  • notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待

  • notifyAll ():唤醒正在排队等待资源的所有线程结束等待.

  • ⭐这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常。

  • 因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁,因此这三个方法只能在Object类中声明。

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
42
43
44
45
package pers.dhx_.java0602;

/**
* @author Dhx_
* @className CommunicationTest
* @description TODO使用两个线程打印 1-100。线程1,线程2 交替打印
* @date 2022/6/3 17:21
* wait() :当前线程进入阻塞状态,并且释放同步监视器(意味着别的线程可以 进入)
* notify() :唤醒一个 被wait() 的线程,如果有多个 线程 被wait就唤醒优先级最高的那个线程
*notifyAll():所有唤醒被wait() 的线程
*/
public class CommunicationTest {
public static void main(String[] args) {
Number x1 = new Number();
Thread t1 = new Thread(x1);
Thread t2 = new Thread(x1);
t1.setName("Thread 1");
t2.setName("Thread 2");
t1.start();
t2.start();
}
}

class Number extends Thread {
int Number = 1;

@Override
public void run() {
while (true) {
synchronized (this) {
notify();
if (Number <= 100) {
System.out.println(Thread.currentThread().getName() + ": " + Number++);
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else break;
}
}
}
}


🌟案例: 生产者/消费者问题

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package pers.dhx_.java0602;

/**
* @author Dhx_
* @className ProduceTest
* @description TODO 线程通信的 应用 :经典例题:生产者/消费者问题
* @date 2022/6/3 18:24
* 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处
* 取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图
* 生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通
* 知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如
* 果店中有产品了再通知消费者来取走产品。
* 这里可能出现两个问题:
* 生产者比消费者快时,消费者会漏掉一些数据没有取到。
* 消费者比生产者快时,消费者会取相同的数据。
* <p>
* 关键在于 给produceProduct() 以及consumeProduct() 都加了synchronized ,
* 否则 同时进行可能会java.lang.IllegalMonitorStateException ,
* 并且 product==0 时,也购买 ,显然不合理
*/
public class ProduceTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer p1 = new Producer(clerk);
p1.setName("生产者1 ");
Consumer c1 = new Consumer(clerk);
c1.setName("消费者1 ");
p1.start();
c1.start();
// try {
// Thread.sleep(5000);
// System.exit(0);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}

class Clerk {
private int num;

public synchronized void produceProduct() { //生产 product
if (num < 20) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
Thread.currentThread().getName() + " 正在生产 第" + (num++) + "个 产品");
notify();

} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public synchronized void consumeProduct() {//消费 product
if (num > 1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
Thread.currentThread().getName() + " 正在消费 第" + (num--) + "个 产品");
this.notify();

} else {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

class Producer extends Thread {
private Clerk clerk;

public Producer(Clerk c) {
this.clerk = c;
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始生产 ");
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}

//customer 顾客
//consumer 消费者
class Consumer extends Thread {
private Clerk clerk;

public Consumer(Clerk c) {
this.clerk = c;
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始消费 ");
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}

💋 JDK5.0新增线程创建方式

😛实现Callable接口

  • 与使用Runnable相比, Callable功能更强大些

    1. 相比run()方法,可以有返回值
    2. 方法可以抛出异常
    3. 支持泛型的返回值
    4. 需要借助FutureTask类,比如获取返回结果
  • Future接口

    1. 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
    2. Future是Future接口的唯一的实现类
    3. FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package pers.dhx_.java0602;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
* @author Dhx_
* @className CallableTest
* @description TODO使用Callable接口 实现 多线程 -------JDK5.0
* @date 2022/6/3 19:29
* 步骤
* 1.创建一个类 实现Callable 接口,
* 2.实现call方法,将此线程需要做的 操作 code 在方法内
* 3.创建 该类的对象,
* 4。将此对象 作为参数 传递到FutureTask构造器中,创建FutureTask的对象,
* 5.将FutureTask的对象 作为参数传递到 Thread构造器中,双肩Thread对象, 并启动start();
* ----如何理解Callable 比Runnable强大
* Call 可以有返回值,可以抛出异常 ,可以支持泛型
*/
public class CallableTest {
public static void main(String[] args) {
NewThread_1 t1 = new NewThread_1();
// try {
// t1.call();
// } catch (Exception e) {
// e.printStackTrace();
// }
FutureTask futureTask = new FutureTask(t1);
new Thread(futureTask).start();
//get 返回值即为FutureTask构造器参数 Callable对象
try {
Object sum = futureTask.get();
System.out.println(sum);

} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

}
}

class NewThread_1 implements Callable<Object> {
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(i);
sum += i;
}
}
return sum; //装箱 Integer
}

}

😛使用线程池

  • **背景:**经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,

对性能影响很大。

  • **思路:**提前创建好多个线程,放入线程池中,使用时直接获取,使用完

放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交

通工具。

  • 好处:

    1. 提高响应速度(减少了创建新线程的时间)

    2. 降低资源消耗(重复利用线程池中线程,不需要每次都创建)

    3. 便于线程管理

  • corePoolSize:核心池的大小

  • maximumPoolSize:最大线程数

  • keepAliveTime:线程没有任务时最多保持多长时间后会终止

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
42
43
44
45
46
47
48
49
package src.SUMMER_java.common;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import java.util.concurrent.ThreadPoolExecutor;

public class Day0529 {

// public interface ExecutorService
public static void main(String[] args) {
// 1.提供指定数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
/*
* service 的类型时是interface ,接口中只能有常量,不可修改
* but service1 的类型是ThreadPoolExecutor(class)
*/
// 设置线程池的属性:
System.out.println(service.getClass()); // java.util.concurrent.ThreadPoolExecutor
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
service1.getMaximumPoolSize();
// 2. 执行指定线程的操作,需要提供实现Runnable or Callable接口的对象
service.execute(new NumberThread());// 适合使用于Runnable
service.execute(new NumberThread1());// 适合使用于Runnable
// service.submit();//适合使用于 Callable
service.shutdown();
}
}

class NumberThread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0)
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}

class NumberThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0)
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}