多线程
基本概念
线程:线程是操作系统能够进行运行调度的最小单位,它被包含在进程中,是进程中的实际运作单位 简单理解:应用软件中互相独立,可以同时运行的功能
进程:进程是操作系统进行资源分配的单位
并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行
应用场景:
软件中的耗时操作,后台服务器等
多线程的实现方式:
继承Thread类的方式进行实现:
package com.lazyGirl.threaddemo;
public class ThreadDemo extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + " i=" + i);
}
}
}
package com.lazyGirl.threaddemo;
public class Test1 {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
ThreadDemo threadDemo1 = new ThreadDemo();
threadDemo.setName("线程1");
threadDemo1.setName("线程2");
threadDemo.start();
threadDemo1.start();
}
}
输出:
实现Runnable接口的方式进行实现:
package com.lazyGirl.threaddemo;
public class RunnableDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " i:" + i );
}
}
}
package com.lazyGirl.threaddemo;
public class Test2 {
public static void main(String[] args) {
RunnableDemo rd = new RunnableDemo();
Thread t1 = new Thread(rd);
Thread t2 = new Thread(rd);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
输出:
利用Callable接口和Future接口方式实现:
可以获取多线程运行的结果
步骤:
创建一个类实现Callable接口,MyCallable
重写Call方法(有返回值,表示多线程运行的结果)
创建MyCallable对象(表示多线程要执行的任务)
创建FutureTask的对象(管理多线程运行的结果)
创建Thread类的对象,并启动(表示线程)
package com.lazyGirl.threaddemo;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableDemo callableDemo = new CallableDemo();
FutureTask<Integer> futureTask = new FutureTask<>(callableDemo);
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}
package com.lazyGirl.threaddemo;
import java.util.concurrent.Callable;
public class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
输出:
对比:
图来自黑马程序员网课
常见成员方法:
图来自黑马程序员网课
抢占式调度:随机
Java中线程优先级为1-10,1最小,10最大,默认为5
线程的生命周期:
图来自黑马程序员
线程安全问题:
package com.lazyGirl.threaddemo;
public class ThreadDemo2 extends Thread {
static int ticket = 0;
@Override
public void run() {
while (true) {
if (ticket < 100){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(getName() + " ticket is " + ticket);
}else {
break;
}
}
}
}
package com.lazyGirl.threaddemo;
public class Test5 {
public static void main(String[] args) {
ThreadDemo2 threadDemo2 = new ThreadDemo2();
ThreadDemo2 threadDemo3 = new ThreadDemo2();
ThreadDemo2 threadDemo4 = new ThreadDemo2();
threadDemo2.setName("窗口1");
threadDemo3.setName("窗口2");
threadDemo4.setName("窗口3");
threadDemo2.start();
threadDemo3.start();
threadDemo4.start();
}
}
输出:
解决方式:
同步代码块:
把操作共享数据的代码锁起来
图来自黑马程序员网课
package com.lazyGirl.threaddemo;
public class ThreadDemo2 extends Thread {
static int ticket = 0;
static Object lock = new Object();
@Override
public void run() {
while (true) {
synchronized (lock) {
if (ticket < 100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(getName() + " ticket is " + ticket);
}else {
break;
}
}
}
}
}
输出:
锁对象要唯一
它是唯一的
同步方法:
把synchronized关键字加到方法上
图来自黑马程序员网课
package com.lazyGirl.threaddemo;
public class MyRunable implements Runnable{
int ticket = 0;
@Override
public void run() {
while(true){
if (extracted()) break;
}
}
private synchronized boolean extracted() {
if (ticket == 100) {
return true;
}else {
ticket++;
System.out.println(Thread.currentThread().getName() + " ticket is " + ticket);
}
return false;
}
}
输出:
StringBuilder的实例是不安全的,如果需要这样的同步,则使用StringBuffer
锁:
void lock():获得锁
void unlock():释放锁
图来自黑马程序员网课
package com.lazyGirl.threaddemo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadDemo2 extends Thread {
static int ticket = 0;
static Lock lock = new ReentrantLock();
// static Object lock = new Object();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (ticket == 100) {
break;
}else {
Thread.sleep(10);
ticket++;
System.out.println(getName() + " " + ticket);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
输出:
生产者和消费者(等待唤醒机制):
常见方法:
图来自黑马程序员网课
吃货代码:
package com.lazyGirl.waitandnotify;
public class Foodie extends Thread{
@Override
public void run() {
while(true){
synchronized(Desk.lock){
if (Desk.count == 0){
break;
}else {
if (Desk.foodFlag == 0){
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
Desk.count--;
System.out.println("吃货在吃面条,还能再吃 " + Desk.count + "碗");
Desk.lock.notify();
Desk.foodFlag = 0;
}
}
}
}
}
}
厨师代码:
package com.lazyGirl.waitandnotify;
public class Cook extends Thread {
@Override
public void run() {
while (true){
synchronized (Desk.lock) {
if (Desk.count == 0){
break;
}else {
if (Desk.foodFlag == 1){
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
System.out.println("厨师做饭");
Desk.foodFlag = 1;
Desk.lock.notifyAll();
}
}
}
}
}
}
测试类:
package com.lazyGirl.waitandnotify;
public class Demo1 {
public static void main(String[] args) {
Cook c = new Cook();
Foodie f = new Foodie();
c.setName("厨师");
f.setName("吃货");
c.start();
f.start();
}
}
输出:
等待唤醒机制(阻塞队列方式实现)
阻塞队列的继承结构:
图来自黑马程序员网课
package com.lazyGirl.waitandnotifydemo2;
import java.util.concurrent.ArrayBlockingQueue;
public class Cook extends Thread{
ArrayBlockingQueue<String> queue;
public Cook(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while(true){
try {
queue.put("noodle");
System.out.println("厨师放了一碗面条");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
package com.lazyGirl.waitandnotifydemo2;
import java.util.concurrent.ArrayBlockingQueue;
public class Foodie extends Thread{
ArrayBlockingQueue<String> queue;
public Foodie(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true){
try {
String food = queue.take();
System.out.println(food);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
package com.lazyGirl.waitandnotifydemo2;
import java.util.concurrent.ArrayBlockingQueue;
public class Test {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
Cook c = new Cook(queue);
Foodie f = new Foodie(queue);
c.start();
f.start();
}
}
线程的状态:
图来自黑马程序员网课
图来自黑马程序员网课
Case1:
抢红包:
package com.lazyGirl.waitandnotifydemo2.Test4case1;
import java.util.Random;
public class Test extends Thread{
static double money = 100;
static int count = 3;
static final double MIN = 0.01;
@Override
public void run() {
synchronized (Test.class) {
if (count == 0) {
System.out.println(getName() + "没有抢到红包");
}else {
double prize = 0;
if (count == 1) {
prize = money;
}else {
Random random = new Random();
double bounds = money - (count -1) * MIN;
prize = random.nextDouble(bounds);
if (prize < MIN) {
prize = MIN;
}
}
money -= prize;
count--;
System.out.println(getName() + "抢到了 " + prize + "元");
}
}
}
}
package com.lazyGirl.waitandnotifydemo2.Test4case1;
public class MyThread {
public static void main(String[] args) {
Test t1 = new Test();
Test t2 = new Test();
Test t3 = new Test();
Test t4 = new Test();
Test t5 = new Test();
t1.setName("h");
t2.setName("hh");
t3.setName("hhh");
t4.setName("hhhh");
t5.setName("hhhhh");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
输出:
内存分析
图来自黑马程序员网课
package com.lazyGirl.waitandnotifydemo2.test7case;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
ArrayList<Integer> list;
public MyCallable(ArrayList<Integer> list){
this.list = list;
}
@Override
public Integer call() throws Exception {
ArrayList<Integer> boxList = new ArrayList<>();
while(true){
Thread.sleep(10);
synchronized (MyCallable.class){
if (list.size() == 0){
System.out.println(Thread.currentThread().getName() + boxList);
break;
}else {
Collections.shuffle(list);
int prize = list.remove(0);
// System.out.println(getName() + "又产生了一个" + prize + "元大奖");
boxList.add(prize);
}
}
}
if (boxList.size() == 0){
return null;
}else {
return Collections.max(boxList);
}
}
}
package com.lazyGirl.waitandnotifydemo2.test7case;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,10,20,30,40,50);
//创建多线程要运行的参数对象
MyCallable t = new MyCallable(list);
//创建多线程运行结果的管理者对象
FutureTask<Integer> ft1 = new FutureTask<>(t);
FutureTask<Integer> ft2 = new FutureTask<>(t);
Thread t1 = new Thread(ft1);
Thread t2 = new Thread(ft2);
t1.setName("抽奖箱1");
t2.setName("抽奖箱2");
t1.start();
t2.start();
Integer max1 = ft1.get();
Integer max2 = ft2.get();
System.out.println(max1);
System.out.println(max2);
if (max1 > max2){
System.out.println(t1.getName() + "获得" + max1 + "大奖");
}else if (max1 < max2){
System.out.println(t1.getName() + "获得" + max2 + "大奖");
}
}
}
输出:
线程池:
核心原理:
1)创建一个池子,池子中是空的
2)提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子,下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
3)所有任务全部执行完毕,关闭线程池
代码实现:
Executors:线程池的工具类通过调用方法返回不同类型的线程池对象
图来自黑马程序员网课
package com.lazyGirl.threadpooldemo;
public class MyRunnable implements Runnable {
@Override
public void run() {
// for (int i = 0; i < 10; i++) {
// System.out.println(Thread.currentThread().getName() + ":" + i);
// }
System.out.println(Thread.currentThread().getName());
}
}
package com.lazyGirl.threadpooldemo;
import java.lang.reflect.Executable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool1 = Executors.newFixedThreadPool(3);
pool1.submit(new MyRunnable());
Thread.sleep(1000);
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.shutdown();
}
}
输出:
自定义线程池:
图来自黑马程序员网课
图来自黑马程序员网课
线程池大小:
CPU密集型运算: 最大并行数 + 1
I/O密集型运算: 最大并行数 * 期望CPU利用率 *(总时间(CPU计算时间 + 等待时间)/ CPU计算时间)
图来自黑马程序员网课