线程问题-Java

第一章 线程

1.1 创建多线程类对象

  • Java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java通过继承Thread类来创建并启动多线程的步骤:
    1. 定义Thread类的子类,并重写该类的run()方法,该方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体
    2. 创建Thread子类的实例,即创建线程对象
    3. 调用线程对象的start()方法启动线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0;i < 5;i++){
System.out.println("run:"+i);
}
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for (int i = 0;i < 5;i++){
System.out.println("main:"+i);
}
}
  • 相关方法

1
2
3
4
5
6
7
8
9
10
11
//获取当前执行线程的名称
System.out.println(Thread.currentThread().getName());
//疫苗打印一个数字
for (int i = 0;i < 5;i++){
System.out.println("main:"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

1.2 创建线程实现Runable接口

  • 实现步骤
    1. 创建一个Runable接口的实现类,在实现类中重写run方法,设置线程任务
    2. 创建Runable接口的实现类对象
    3. 创建Thread类对象,构造方法中传递Runable接口的实现类对象,调用start开启线程
1
2
3
4
5
6
7
8
9
10
11
12
public class MyRunable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
MyRunable run = new MyRunable();
Thread thread = new Thread(run);
thread.start();
System.out.println(Thread.currentThread().getName());
}
  • 实现Runable接口创建多线程的好处
    1. 避免了单继承的局限性:一个类只能继承一个类,类继承了Thread类就不能继承其他的类但是实现了Runable接口,还是可以继承其他类,实现其他的接口
    2. **增强了程序的扩展性,降低了程序的耦合性(解耦)**:实现Runable接口的方式,把设置线程任务和开启新线程进行了分离。实现类中重写了run方法:用来设置线程任务;创建Thread类对象,调用start方法:用来开启新线程

1.3 匿名内部类方式实现线程的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}.start();

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

第二章 线程安全

  • 多线程访问了共享的数据会容易产生线程安全问题,Java中提供了同步机制(synchronized)

2.1 同步代码块

  • synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问

    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
27
public class MyRunable implements Runnable {
private int ticket = 100;
//创建一个锁对象,可以是任意类型
Object obj = new Object();

@Override
public void run() {
while (true) {
synchronized (obj){
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket + "张票");
ticket--;
}
}
}
}
}
public static void main(String[] args) {
//创建三个线程,同时开启,对共享的票出售
MyRunable run = new MyRunable();
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
Thread t3 = new Thread(run);
t1.start();
t2.start();
t3.start();
}

2.2 同步方法

  • 使用synchronized修饰的方法。保证A线程执行该方法的时候,其他线程只能在方法外等待

    public synchronized void method(){ //........... }

  • 同步锁:对于非静态方法是this;对于static方法,是当前方法所在字节码对象(类名.class)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyRunable implements Runnable {
private int ticket = 100;
public synchronized void method(){
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket + "张票");
ticket--;
}
}
@Override
public void run() {
while (true) {
method();
}
}
}

2.3 Lock锁机制

  • public void lock():加同步锁
  • public void unlock():释放同步锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyRunable implements Runnable {
private int ticket = 100;
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();
if (ticket > 0) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket + "张票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
l.unlock();//无论是否出现安全问题都会释放锁对象
}
}
}
}
}

3 线程池

  • 使用线程池的工厂类Excutors里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
  • 创建一个类,实现Runable接口,重写run方法设置线程任务
  • 调用ExcutorService的方法submit,传递线程任务(实现类),开启线程
1
2
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new MyRunable());