喵星之旅-成长的雏鹰-java高级特性-4-多线程

多线程

什么是多线程:
1、如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”
2、多个线程交替占用CPU资源,而非真正的并行执行

多线程好处:
1、充分利用CPU的资源
2、简化编程模型
3、带来良好的用户体验

主线程

Thread类:Java提供了java.lang.Thread类支持多线程编程。

主线程:
1、main()方法即为主线程入口
2、产生其他子线程的线程
3、必须最后完成执行,因为它执行各种关闭动作

线程的创建和启动

在Java中创建线程的两种方式
继承java.lang.Thread类
实现java.lang.Runnable接口

具体实现可以参考Thread类文档注释。

继承类创建线程

定义MyThread类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyThread extends Thread{
//重写run()方法
public void run(){
for(int i=1;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}


public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); //启动线程
}

实现接口创建线程

定义MyRunnable类实现Runnable接口
实现run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyRunnable implements Runnable{
public void run(){
for(int i=1;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}


public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread myThread = new Thread(myRunnable);
thread.start(); //启动线程
}

run()和start()

调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();
直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。
一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.llegalThreadStateException 异常;
run() 方法没有限制。

线程的状态

新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

常见方法

对象的方法

1 public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

2 public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。

3 public final void setName(String name)
改变线程名称,使之与参数 name 相同。

4 public final void setPriority(int priority)
更改线程的优先级。

1
2
3
4
5
6
7
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

5 public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。

6 public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。

7 public void interrupt()
中断线程。

8 public final boolean isAlive()
测试线程是否处于活动状态。

静态方法

1 public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。

2 public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

3 public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。

4 public static Thread currentThread()
返回对当前正在执行的线程对象的引用。

5 public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。

线程安全

同步方法

使用synchronized修饰的方法控制对类成员变量的访问

1
2
访问修饰符 synchronized 返回类型 方法名(参数列表){……}

同步前:

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
public class Demo05 {
int n = 2000;
public void jianshao() {
n--;
}

public static void main(String[] args) {

Demo05 d = new Demo05();
System.out.println(d.n);
Thread t1 = new Foo(d);
Thread t2 = new Foo(d);
t1.start();
t2.start();

try {
t1.join();
t2.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

System.out.println(d.n);

}
}

同步后:

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
public class Demo05 {
int n = 2000;
public synchronized void jianshao() {
n--;
}


public static void main(String[] args) {


Demo05 d = new Demo05();
System.out.println(d.n);
Thread t1 = new Foo(d);
Thread t2 = new Foo(d);
t1.start();
t2.start();

try {
t1.join();
t2.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

System.out.println(d.n);

}
}

同步前,运行结果不定,可能是0,也可能是大于0小于2000的任何数,同步后固定结果为0。

同步代码块

使用synchronized关键字修饰的代码块

1
2
3
4
synchronized(syncObject){
//需要同步的代码
}

syncObject为需同步的对象,通常为this
效果与同步方法相同。

多个并发线程访问同一资源的同步代码块时:
1、同一时刻只能有一个线程进入synchronized(this)同步代码块
2、当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
3、当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码

文章目录
  1. 多线程
    1. 主线程
    2. 线程的创建和启动
  2. 继承类创建线程
  3. 实现接口创建线程
  4. run()和start()
  5. 线程的状态
  6. 常见方法
    1. 对象的方法
    2. 静态方法
  7. 线程安全
    1. 同步方法
    2. 同步代码块
|