多线程
并发与并行
- 并发:指两个或多个事件在同一个时间段内发生(某一时刻只有一个程序在执行)。
- 并行:指两个或多个事件在同一时刻发生(同时发生,某一时刻可能有多个程序在执行)。
进程与线程
-
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
-
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
一个进程可以包含多个线程。
创建多线程
继承Thread
创建一个类,继承自Thread,重写run()方法,run()方法中的内容就是线程启动后执行的内容。使用start()方法启动线程。
MyThread.java
public class MyThread extends Thread{
@Override
public void run(){
System.out.println("创建线程");
}
}
Main.java
public class Main {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
实现Runnable接口
创建一个类,实现Runnable接口,重写接口中的Run()方法,然后创建一个Thread类,将该类的对象传入。
好处:
- 避免了单继承的局限性
- 增强了程序的可扩展性,解耦,将设置线程任务和开启线程进行分离。
MyRunnable.java
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("实现Runnable接口,创建线程");
}
}
Main.java
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
匿名内部类
第二种写法可以使用匿名内部类
public class Main {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类创建线程");
}
}).start();
}
}
该种写法可以简化
public class Main {
public static void main(String[] args) {
new Thread(() ->{
System.out.println("匿名内部类创建线程");
}).start();
}
}
线程安全问题
现在有一个方法输出1到100,用三个线程来实现,使用以下代码
MyRunnable.java
import static java.lang.Thread.sleep;
public class MyRunnable implements Runnable{
private int i=1;
@Override
public void run() {
while(i<=100) {
System.out.println(Thread.currentThread().getName() + "执行输出" +i);
//增加出现线程安全的概率
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
Main.java
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
Thread thread3 = new Thread(myRunnable);
thread1.start();
thread2.start();
thread3.start();
}
}
输出的内容中可以看到有多个1输出,出现了线程安全问题,原因就是一个线程执行到 System.out.println(Thread.currentThread().getName() + "执行输出" +i);
时,还没有执行i++
,另一线程也执行到System.out.println(Thread.currentThread().getName() + "执行输出" +i);
导致输出了多个相同的数。
同步代码块
使用方法:
synchronized(锁对象) {
可能会出现线程安全的代码
}
锁对象可以是任何对象。
import static java.lang.Thread.sleep;
public class MyRunnable implements Runnable{
private int i=1;
static Object o = new Object();
@Override
public void run() {
synchronized (o) {
while(i<=100) {
System.out.println(Thread.currentThread().getName() + "执行输出" +i);
//增加出现线程安全的概率
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
}
同步方法
在方法名前加synchronized关键字
锁对象:this
使用方法:
修饰符 synchronized 返回值类型 函数名(){
可能会出现线程安全的代码
}
import static java.lang.Thread.sleep;
public class MyRunnable implements Runnable{
private int i=1;
static Object o = new Object();
@Override
public void run() {
printNum();
}
private synchronized void printNum() {
while(i<=100) {
System.out.println(Thread.currentThread().getName() + "执行输出" +i);
//增加出现线程安全的概率
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
使用静态方法
将可能出现线程安全问题的方法设置为静态方法。
所对象:.class文件
import static java.lang.Thread.sleep;
public class MyRunnable implements Runnable{
private static int i=1;
@Override
public void run() {
printNum();
}
private static void printNum() {
while(i<=100) {
System.out.println(Thread.currentThread().getName() + "执行输出" +i);
//增加出现线程安全的概率
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
lock锁
使用方法:
创建ReentrantLock对象,在可能出现线程安全问题的代码前面调用lock()方法,在代码后调用unlock()方法。
import java.util.concurrent.locks.ReentrantLock;
import static java.lang.Thread.sleep;
public class MyRunnable implements Runnable{
private static int i=1;
ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
reentrantLock.lock();
while(i<=100) {
System.out.println(Thread.currentThread().getName() + "执行输出" +i);
//增加出现线程安全的概率
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
reentrantLock.unlock();
}
}