Java中的多线程

多线程的创建有3种方法

  • 继承 Thread 类,重写 run()方法,run()方法代表线程要执行的任务。
  • 实现 Runnable 接口,重写 run()方法,run()方法代表线程要执行的任务。
    • 实现Runnable接口方法存在的必要性?
    • java不支持多继承
    • 不打算重写Thread类的其他方法
  • 实现 callable 接口,重写 call()方法,call()作为线程的执行体,具有返回值,并且可以
    对异常进行声明和抛出;使用 start()方法来启动线程

Thread类

构造方法 说明
Thread() 创建一个线程对象
Thread(String name) 创建一个具有指定名称的线程对象
Thread(Runnable target) 创建一个基于Runnable接口实现类的线程对象
Thread(Runnable target, String name) 创建一个基于Runnable接口实现类,并且具有指定名称的线程对象
常用方法 说明
public void run() 线程相关的代码写在该方法中,一般需要重写
public void start() 启动线程的方法
public static void sleep(long n) 线程休眠m毫秒的办法
public void join() 优先执行调用join()方法的线程,阻塞其他线程,执行本线程

Runnable接口

  • 只有一个方法run()
  • Runnable是java中实现线程的接口
  • 任何实现线程功能的类都必须实现该接口

线程的状态

  • 新建(New)
  • 可运行(Runnable)
  • 正在运行(Running)
  • 阻塞(Blocked)
  • 终止(Dead)

线程的生命周期

sleep方法

public static void sleep(long millis)
作用:在指定的毫秒数内让正在执行的线程休眠(暂停执行)参数为休眠的时间,单位是毫秒

join方法应用

public final void join()
作用:等待调用该方法的线程结束后才能执行
public final void jion(long millis)
作用:等待该线程终止的最长时间为millis毫秒,如果millis为0,意味着要一直等下去

线程优先级

  • java为线程类提供10个优先级
  • 优先级可以用整数1-10表示,超过范围会抛出异常
  • 主线程默认优先级为5
  • 优先级常量
    • MAX_PRIORITY:线程的最高优先级10
    • MIN_PRIORITY:线程的最低优先级1
    • NORM_PRIOITY:线程的默认优先级5
  • 优先级相关方法
    • public int getPriority(): 获取线程优先级的方法
    • public void setPriority(int newPriority):设置线程优先级方法

多线程运行时的随机问题

  • 各个线程是通过竞争CPU时间而获得运行机会的
  • 各线程什么时候得到CPU时间,占用多久,是不可预测的
  • 一个正在运行的线程在什么地方被暂停是不确定的

同步

  • synchronized关键字可以用在

    • 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
    • 成员方法 public synchronized void saveAccount(){}
      被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象
    • 静态方法public static synchronized void saveAccount(){}
      其作用的范围是整个静态方法,作用的对象是这个类的所有对象
    • 语句块synchronized (obj){......}
      被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  • 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。

  • 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
  • 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

线程间通信

  • wait():中断方法的执行,使线程等待
  • notify()随机唤醒处于等待的某一个线程,使其结束等待
  • notifyAll():唤醒所有处于等待的线程,使他们结束等待

实现Callable接口创建线程

  1. 创建 Callable 接口的实现类,并实现 call()方法,该 call()方法将作为线程执行体,并且 有返回值。
  2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call()方法的返回值。
  3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
  4. 调用 FutureTask 对象的 get()方法来获得子线程执行结束后的返回值。

例子

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
package com.company;

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

/**
* @Description:测试Callable多线程的练习
* @Date: Created in 13:43 2018/2/21
* @Author: Anthony_Duan
*/
public class ThiridThread implements Callable<String >{
@Override
public String call() throws Exception {
String str = "这是用Callable接口实现的多线程";
return str;
}
}

class ThiridThreadTest{

public static void main(String[] args) {
Callable<String> call = new ThiridThread();
FutureTask<String> ft = new FutureTask<>(call);
Thread t = new Thread(ft);
t.start();

try {
System.out.println(ft.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}

银行账户典型案例

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
/**
* 银行账户类
*/
class Account {
String name;
float amount;

public Account(String name, float amount) {
this.name = name;
this.amount = amount;
}
//存钱
public void deposit(float amt) {
amount += amt;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//取钱
public void withdraw(float amt) {
amount -= amt;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public float getBalance() {
return amount;
}
}

/**
* 账户操作类
*/
class AccountOperator implements Runnable{
private Account account;
public AccountOperator(Account account) {
this.account = account;
}

public void run() {
synchronized (account) {
account.deposit(500);
account.withdraw(500);
System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
}
}
}

测试代码

1
2
3
4
5
6
7
8
9
Account account = new Account("zhang san", 10000.0f);
AccountOperator accountOperator = new AccountOperator(account);

final int THREAD_NUM = 5;
Thread threads[] = new Thread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i ++) {
threads[i] = new Thread(accountOperator, "Thread" + i);
threads[i].start();
}

  在AccountOperator 类中的run方法里,我们用synchronized 给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞,直到该线程访问account对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。
结果

Thread3:10000.0
Thread2:10000.0
Thread1:10000.0
Thread4:10000.0
Thread0:10000.0

参考链接http://blog.csdn.net/luoweifu/article/details/46613015

-------------End Of This ArticleThank You For Reading-------------