多线程

基本使用

Thread

方法一,继承Thread

# Worker
public class Worker extends Thread {
public void run() {
// 线程主体声明在重写的run方法内
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
# Main
public class Main {
public static void main(String[] args) {
// 执行时,调用线程类的start方法开启一个线程。此处同时开启三个线程
new Worker().start();
new Worker().start();
new Worker().start();
}
}

注意:开启线程时,调用start方法,如果调用run方法,只是普通方法调用,并不会开启线程。

Runnable

方法二,实现Runnable接口。

# Worker
public class Worker implements Runnable {
public void run() {
// 线程主体声明在重写的run方法内
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
# Main
public class Main {
public static void main(String[] args) {
Worker worker = new Worker();
new Thread(worker).start();
new Thread(worker).start();
new Thread(worker).start();
}
}

此种方法可以用于同时开启同一个对象的多个线程。

匿名类

new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}).start();

线程池

异步计算案例:

/**
* Worker实现Callable接口
*/
public class Worker implements Callable<Integer> {
private int end;
/**
* 构造方法,传递变量
*
* @param end 计算多少数值之和
*/
Worker(int end) {
this.end = end;
}
/**
* 重写call方法,计算求和
*
* @return 返回求和结果
*/
@Override
public Integer call() {
int total = 0;
for (int i = 1; i <= end; i++) {
total += i;
}
return total;
}
}
/**
* 主线程
*/
public class Index {
public static void main(String[] args) {
// 初始化线程池
ExecutorService es = Executors.newFixedThreadPool(2);
Worker task1 = new Worker(100);
Worker task2 = new Worker(200);
// 提交任务至线程池,返回Future对象结果
Future<Integer> s1 = es.submit(task1);
Future<Integer> s2 = es.submit(task2);
try {
// 通过Future对象,获取返回的结果
System.out.println(s1.get());
System.out.println(s2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// 关闭线程池
es.shutdown();
}
}

同步锁

线程同步锁使线程数据安全,但是会造成线程等待,导致速度降低。

所以在多线程中,只把需要更新的数据放在同步块里面,并且赋值给其他变量。

同步块外面不能再使用更新数据的变量,避免其他线程已经更新此变量。

同步块里面,尽量只包裹更新的数据,不要处理其他流程,避免影响性能。

synchronized 关键字

例如:

# Tickets 票务类
public class Tickets implements Runnable {
private int m;
private int n = 0;
private final Object obj = new Object();
Tickets(int m) {
this.m = m;
}
@Override
public void run() {
while (true) {
int t;
synchronized (obj) {
// 此处计算n,并且取出值赋给t,同步块外面不再使用n变量,因为n变量在使用时,可能被其他线程更改,所以使用t变量。
t = ++n;
}
if (t > m) {
break;
}
// 处理第now张票
System.out.println(t);
}
}
}
# Main
public static void main(String[] args) {
Tickets tk = new Tickets(10);
new Thread(tk).start();
new Thread(tk).start();
new Thread(tk).start();
}

同步锁的另外一种写法(推荐):

public class Tickets implements Runnable {
private int m;
private int n = 0;
Tickets(int m) {
this.m = m;
}
@Override
public void run() {
int t;
while ((t = pay()) <= m) {
System.out.println(t);
}
}
private synchronized int pay() {
return ++n;
}
}

注意synchronized声明的同步方法默认同步锁是this,静态同步方法的同步锁是类名.class

Lock接口

Lock接口比synchronized关键字更灵活。在有异常处理时,推荐使用lock接口,反正,可以使用synchronized关键字。

示例:

public class Tickets implements Runnable {
private int m;
private int n = 0;
private Lock l = new ReentrantLock();
Tickets(int m) {
this.m = m;
}
@Override
public void run() {
while (true) {
int t = 0;
l.lock();
try {
Thread.sleep(100);
t = ++n;
} catch (Exception e) {
e.printStackTrace();
} finally {
l.unlock();
}
if (t > m) {
break;
}
System.out.println(t);
}
}
}