多线程

用java实现线程程序

  • 继承Thread

  • 重写父类run方法

  • 创建子类对象

  • 子类对象调用start方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package day14;

public class Test02 {
public static void main(String[] args) {
SubThread subThread = new SubThread();
subThread.start();//调用start()启动线程jvm自动调用run
for (int x=0;x<100;x++){
System.out.println("main.."+x);
}
}

}
class SubThread extends Thread{
@Override
public void run() {
//
for (int x=0;x<100;x++){
System.out.println("x.."+x);
}
}
}

实现线程程序中的问题

  • 为什么要继承Thread:

    子类要编程线程对象

  • 为什么要重写run:

    线程要执行什么代码,未知的,run方法就是模板,想让线程执行什么程序,就写在run中

  • 为什么启动线程调用start,而不是run:

    jvm线程操作必须依赖操系统,start启动线程的方法,最终是本地方法c++写的

线程内存图 方法栈

  • 当线程对象执行start后,jvm中会出现一个新的方法栈,run方法进栈
  • CPU将自动从新的栈内存中读取方法执行

线程中的名字

  • 在run中使用super.getName()和super.setName()来获取和设置线程的名字
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test03 {
public static void main(String[] args) {
SubThread1 subThread1 = new SubThread1();
subThread1.start();
}

}
class SubThread1 extends Thread{
@Override
public void run() {
System.out.println("线程名字:"+super.getName());
}
}
  • static Thread currentThread()静态,类名直接调用,返回正在执行的当前线程的对象

    当前线程:方法写在哪,哪里就是当前线程

1
2
3
4
5
public static void main(String[] args) {
//拿到当前线程的对象
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}

设置线程优先级

  • 线程对象.setPriority() 写在start()之前
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
public class Test03 {
public static void main(String[] args) {
SubThread1 subThread0 = new SubThread1();
//设置线程优先级
subThread0.setPriority(Thread.MIN_PRIORITY);
subThread0.start();
SubThread1 subThread1 = new SubThread1();
subThread1.setPriority(Thread.MAX_PRIORITY);
subThread1.start();
}

}
class SubThread0 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}

}
}
class SubThread1 extends Thread{
@Override
public void run() {

for (int i = 0; i < 100; i++) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
}

线程对象的方法join()

  • void join()调用方法的线程,结束后其他线程再运行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package day14;

    public class Test04 {
    public static void main(String[] args) throws InterruptedException {
    SubThread3 subThread3 = new SubThread3();
    subThread3.start();
    subThread3.join();
    //subThread3 运行结束后其他线程才能运行
    Thread thread = Thread.currentThread();
    for (int i = 0; i < 100; i++) {
    System.out.println(thread.getName());
    }
    }
    }
    class SubThread3 extends Thread{
    @Override
    public void run() {
    for (int i = 0; i < 100; i++) {
    Thread thread = Thread.currentThread();
    System.out.println(thread.getName());
    }

    }
    }
  • 进程让步Thread.yield()方法。进程调度相对稳定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package day14;

public class Test04 {
public static void main(String[] args) throws InterruptedException {
SubThread3 subThread1 = new SubThread3();
subThread1.start();
SubThread3 subThread2 = new SubThread3();
subThread2.start();
}
}
class SubThread3 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {

Thread thread = Thread.currentThread();
Thread.yield();
System.out.println(thread.getName());
}

}
}

实现线程程序接口实现

  • 定义类实现接口Runnable接口
  • 重写接口的抽象方法run()
  • 创建Thread类的对象,构造方法传递Runnalbe接口实现类的对象
  • 调用Thread类的方法start()启动线程

接口实现类

1
2
3
4
5
6
7
8
public class SubRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}

运行

1
2
3
4
5
6
7
8
9
public class Test {
public static void main(String[] args) {
//创建接口实现类对象
Runnable subRunnable = new SubRunnable();
//创建Thread类对象,构造方法传接口实现类对象
Thread thread = new Thread(subRunnable);
thread.start();
}
}

使用匿名内部类的形式创建线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {
public static void main(String[] args) {
//Thread的方式
new Thread(){
@Override
public void run() {
System.out.println("线程启动...继承Thread实现");
}
}.start();
//接口的形式
new Thread( new Runnable(){
@Override
public void run() {
System.out.println("线程启动...接口实现");
}
}).start();
}
}

线程安全问题

线程安全问题出现的前提:多个线程同时操作一个共享的资源

100张火车票,3个窗口。售票问题,存在安全隐患。有可能会出现,也可能不出现。

1
2
3
4
5
6
7
8
9
10
11
12
public class Windows implements Runnable{
//定义车票100张
private int ticket=100;
@Override
public void run() {
while(true){
if (ticket>0){
System.out.println("出售第"+ticket--+"张"+"---"+Thread.currentThread().getName());
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {
public static void main(String[] args) {
//创建接口实现类对象
Windows windows = new Windows();
//三个线程共享一个数据
Thread thread0 = new Thread(windows);
Thread thread1 = new Thread(windows);
Thread thread2 = new Thread(windows);
thread0.start();
thread1.start();
thread2.start();
}
}

解决办法:一个线程没有执行完售票,其他线程不能执行。

使用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
public class Windows implements Runnable{
//定义车票100张
private int ticket=100;
private Object obj=new Object();
@Override
public void run() {

while(true){
//使用同步代码块
synchronized (obj){
if (ticket>0){
try {
//线程休眠5毫秒
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("出售第"+ticket--+"张"+"---"+Thread.currentThread().getName());
}
}
}

}
}

同步代码块中写了一个对象,成为对象监视器,简单称为对象锁。保证线程的安全,依靠同步中的对象所在,有锁的线程进入执行,无锁的线程等待在同步代码块的外面。

同步方法

当一个方法中都是共享资源的时候,使用同步方法。否则浪费资源

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
public class Windows implements Runnable{
//定义车票100张
private int ticket=100;
private Object obj=new Object();
@Override
public void run() {

while(true){
sell();
}

}
private synchronized void sell(){
//使用同步代码块
//同步方法是当前对象this作为锁
if (ticket>0){
try {
//线程休眠5毫秒
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("出售第"+ticket--+"张"+"---"+Thread.currentThread().getName());
}

}
}
  • 同步方法中有对象锁吗:

    当前对象作为this锁

  • 静态同步方法中有对象锁吗,静态没有this、super:

    类加载器加载类时,为class文件创建对象

    对象锁是本类的.class文件的对象,拿来直接用

    类名.class

死锁案例

死锁案例的关键:在于多个线程同时争夺一个锁资源,造成程序假死现象。出现在同步代码块的嵌套中。

1
2
3
public class LockA {
public static final LockA lockA=new LockA();
}
1
2
3
public class LockB {
public static final LockB lockB=new LockB();
}
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
public class DeadLock implements Runnable {
private boolean flag;//变量,决定线程进入哪个同步代码块
public DeadLock(boolean flag){
this.flag=flag;
}
@Override
public void run() {
while(true){
if (flag){
//线程先进入同步A锁
synchronized (LockA.lockA){
System.out.println("if..lockA");
//进入同步B锁
synchronized (LockB.lockB){
System.out.println("if..lockB");
}
}
}else {
//先进入同步B锁
synchronized (LockB.lockB){
System.out.println("else..LockB");
//进入同步A锁
synchronized (LockA.lockA){
System.out.println("else..LockA");
}
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
public class Test {
public static void main(String[] args) {
DeadLock deadLock0 = new DeadLock(true);
DeadLock deadLock1 = new DeadLock(false);
Thread thread0 = new Thread(deadLock0);
Thread thread1 = new Thread(deadLock1);
thread0.start();
thread1.start();
}
}

jdk5新特性Lock接口

  • 接口所在包:java.util.concurrent.locks包 JUC

  • 接口实现类ReentrantLock

    方法:获取锁lock(),释放锁unlock()

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
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Windows implements Runnable{
//定义车票100张
private int ticket=100;
//创建Lock接口实现类对象
Lock lock=new ReentrantLock();
@Override
public void run() {

while(true){
sell();
}

}
private void sell(){
lock.lock();
try {
if (ticket>0){
Thread.sleep(5);
System.out.println("出售第"+ticket--+"张"+"---"+Thread.currentThread().getName());
}
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}

}
}