单线程程序只有一个顺序执行流,多线程的程序则可以包括多个顺序执行流,多个顺序流之间互不干扰
现在的操作系统大多数都支持同时运行多个任务,一个任务通常是一个程序,每个运行的程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程
并行指在同一时刻,有多条指令在多个处理器上同时执行
并发指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果
一个程序运行后至少有一个进程,一个进程里可以包含多个线程,但至少包含一个线程
操作系统可以同时执行多个任务,每个任务就是进程,进程可以同时执行多个任务,每个任务就是线程
方式一:继承Thread类重写run方法
package day8;
// 创建线程方式一:继承Thread类,重写run()方法,调用start()方法开启线程
public class ThreadTest1 extends Thread {
public static void main(String[] args) {
// 创建一个线程对象
ThreadTest1 thread = new ThreadTest1();
// 调用start方法
thread.start();
// 主线程
for (int i = 0; i < 200; i++) {
System.out.println("我在学习多线程... " + i);
}
}
@Override
public void run() {
// run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码... " + i);
}
}
}
方式二:实现Runnable接口
package day8;
// 创建线程的方式2:实现Runnable接口,重写run方法
public class ThreadTest3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码... " + i);
}
}
public static void main(String[] args) {
ThreadTest3 threadTest3 = new ThreadTest3();
Thread thread = new Thread(threadTest3);
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println("我在学习多线程... " + i);
}
}
}
方式三:实现Callable接口
package day8;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 线程创建方式三:实现callable接口
public class ThreadTest5 implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码... " + i);
}
return true;
}
public static void main(String[] args) {
ThreadTest5 thread1 = new ThreadTest5();
ThreadTest5 thread2 = new ThreadTest5();
ThreadTest5 thread3 = new ThreadTest5();
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(thread1);
executorService.submit(thread2);
executorService.submit(thread3);
executorService.shutdown();
for (int i = 0; i < 100; i++) {
System.out.println("我在学习多线程... " + i);
}
}
}
在线程的生命周期中,它要经过新建、就绪、运行、阻塞和死亡5中状态
package day8;
/**
* 观察线程的状态
*/
public class ThreadStateTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("=====");
});
// 观察状态
Thread.State state = thread.getState();
System.out.println(state);
thread.start();
state = thread.getState();
System.out.println(state);
while (state != Thread.State.TERMINATED) {
Thread.sleep(100);
state = thread.getState(); // 更新状态
System.out.println(state); // 打印状态
}
}
}
NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
=====
TERMINATED
Process finished with exit code 0
join
package day8;
// 测试join方法
public class ThreadJoinTest implements Runnable {
public static void main(String[] args) throws InterruptedException {
ThreadJoinTest threadJoinTest = new ThreadJoinTest();
Thread thread = new Thread(threadJoinTest);
thread.start();
for (int i = 0; i < 1000; i++) {
if (i == 200) {
thread.join();
}
System.out.println("main " + i);
}
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程VIP来了... " + i);
}
}
}
sleep
package day8;
/**
* 模拟网络延时
*/
public class ThreadSleepTest1 implements Runnable {
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
// 模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": " + ticketNums--);
}
}
public static void main(String[] args) {
ThreadSleepTest1 threadSleepTest1 = new ThreadSleepTest1();
new Thread(threadSleepTest1, "A").start();
new Thread(threadSleepTest1, "B").start();
}
}
yield
package day8;
// 测试线程礼让
// 礼让不一定成功
public class ThreadYieldTest {
public static void main(String[] args) {
MyThread thread = new MyThread();
new Thread(thread, "A").start();
new Thread(thread, "B").start();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程开始执行。。。");
Thread.yield();
System.out.println(Thread.currentThread().getName() + " 线程停止执行。。。");
}
}
同步代码块
synchronized(obj) {
...
}
任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定
同步方法
与同步代码块对应,Java多线程安全支持还提供了同步方法,同步方法是使用synchronized关键字来修饰某个方法
public synchronized void play() {
...
}
Lock提供了比synchronized更加广泛的锁定操作,Lock允许实现灵活的结构,可以具有差别很大的属性,并且支持多个相关的Condition对象
package day8;
import java.util.concurrent.locks.ReentrantLock;
/**
* 账户取钱
*/
public class LockTest {
public static void main(String[] args) {
MyAccount myAccount = new MyAccount();
for (int i = 0; i < 5; i++) {
new Thread(myAccount, "线程" + i).start();
}
}
}
class MyAccount implements Runnable {
private final ReentrantLock lock = new ReentrantLock();
private int money = 1000;
private void draw() {
lock.lock();
try {
if (money > 0) {
System.out.println(Thread.currentThread().getName() + " --> 取钱成功!");
money -= 500;
System.out.println("余额为:" + money);
// 模拟取钱的延时
Thread.sleep(1000);
} else {
System.out.println(Thread.currentThread().getName() + " --> 取钱失败,余额不足!");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
@Override
public void run() {
draw();
}
}
线程0 --> 取钱成功!
余额为:500
线程2 --> 取钱成功!
余额为:0
线程1 --> 取钱失败,余额不足!
线程3 --> 取钱失败,余额不足!
线程4 --> 取钱失败,余额不足!
Process finished with exit code 0
传统的线程通信
可以借助Object类提供的wait()、notify()和notifyAll()三个方法,但这三个方法必须由同步监视器对象来调用
package day8;
import java.util.LinkedList;
public class ThreadCommunicationTest {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
new P(list).start();
new C(list).start();
}
}
class P extends Thread {
final LinkedList<String> linkedList;
public P(LinkedList<String> list) {
super("生产者");
this.linkedList = list;
}
@Override
public void run() {
String[] strings = new String[]{"Java", "C++", "C", "Python"};
for (int i = 0; i < 99; i++) {
// 模拟生产延时
try {
sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (linkedList) {
String item = strings[i % strings.length];
linkedList.add(item);
System.out.println(getName() + "生产了" + item);
linkedList.notify(); // 通知消费者消费
}
}
}
}
class C extends Thread {
final LinkedList<String> linkedList;
public C(LinkedList<String> list) {
super("消费者");
this.linkedList = list;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (linkedList) {
if (linkedList.size() == 0) {
try {
linkedList.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + "消费了" + linkedList.pollLast());
}
}
}
}
使用阻塞队列控制线程通信
package day8;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueTest {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(4);
for (int i = 0; i < 5; i++) {
new Producer(blockingQueue).start();
}
new Consumer(blockingQueue).start();
}
}
class Producer extends Thread {
private BlockingQueue<String> blockingQueue;
public Producer(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
String[] strings = new String[]{"Java", "C++", "C", "Python"};
System.out.println(getName()+"开始生产了...");
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(500);
blockingQueue.put(strings[i % strings.length]);
System.out.println(getName() + "生产了" + strings[i % strings.length]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread {
private BlockingQueue<String> blockingQueue;
public Consumer(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(100);
System.out.println(getName() + "消费了" + blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
系统启动一个新线程的成本比较高,因为它涉及与操作系统交互。在这种情况下,使用线程池可以很好地提高性能
使用线程池来执行线程任务的步骤
1、创建线程池
2、创建线程执行任务
3、将执行任务提交给线程池
4、关闭线程池
package day8;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) throws Exception {
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(8);
// 创建执行任务
Runnable target = () -> {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "--> " + i);
}
};
// 提交执行任务
executorService.submit(target);
executorService.submit(target);
// 关闭线程池
executorService.shutdown();
}
}