线程学习
参考链接:
https://www.zhihu.com/question/264627396
https://blog.csdn.net/weixin_43249530/article/details/88064361
https://blog.csdn.net/rjkkaikai/article/details/81016445
https://blog.csdn.net/m0_45067620/article/details/108026299
https://www.cnblogs.com/dolphin0520/p/3920385.html
1、线程和进程
进程
进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。
在Java中,当启动main方法时其实就是启动了一个JVM的进程,而main方法所在的线程就是这个进程的一个线程,也称主线程。
线程
线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进行不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和方法栈,所以系统在产生一个线程,或者在各个线程之间作切換工作时,负担要比进程小得多,也正因为如此,线程也被成为轻量级进程。
2、线程的创建
package com.example.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Demo01 {
public static void main(String[] args) {
// 1、继承Thread创建线程
MyThread myThread = new MyThread();
myThread.start();
// 2、实现Runnable接口创建线程
new Thread(new MyThread_1()).start();
// 3、实现Callable接口创建线程
FutureTask<String> futureTask = new FutureTask(new MyThread_2());
new Thread(futureTask).start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("继承Thread创建线程");
}
}
class MyThread_1 implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable接口");
}
}
class MyThread_2 implements Callable<String> {
@Override
public String call() throws Exception {
return "实现Callable接口";
}
}
3、线程的方法
sleep方法
sleep为线程休眠方法,单位为毫秒,休眠时由运行态到阻塞态,休眠结束后由阻塞态到就绪态
线程休眠:指的是让线程暂缓执行一下,等到了预计时间之后再恢复执行。线程休眠会立马交出CPU,让CPU去执行其他的任务。注意:sleep方法不会释放锁,意思是如果当前线程持有对某个对象的锁,即使调用sleep方法,其他线程也无法访问这个对象。
package com.example.thread;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
new Thread(myThread_3, "小明").start();
new Thread(myThread_3, "小红").start();
}
}
class MyThread_3 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "延时前");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "延时后");
}
}
结果

yield方法
线程礼让方法,由运行态到就绪态,停止一下后再由就绪态到运行态,不会释放锁。
yield让当前运行线程回到就绪态,允许具有相同优先级的其他线程获得运行机会。目的是让相同优先级的线程之间能适当的轮转执行,但实际中无法保证yield达到礼让目的,因为礼让的线程还有可能被线程调度程序再次选中。
package com.example.thread;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
new Thread(myThread_3, "小明").start();
new Thread(myThread_3, "小红").start();
new Thread(myThread_3, "小方").start();
}
}
class MyThread_3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
Thread.yield();
System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
}
}
}
结果:

join方法
让“主线程”等待“子线程”结束之后才能继续运行,会释放锁
没加join的方法
package com.example.thread;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
Thread thread = new Thread(myThread_3, "小明");
thread.start();
System.out.println("当前线程:" + Thread.currentThread().getName());
}
}
class MyThread_3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
}
}
}
结果

加join的方法
package com.example.thread;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
Thread thread = new Thread(myThread_3, "小明");
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程:" + Thread.currentThread().getName());
}
}
class MyThread_3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
}
}
}
结果

获取状态方法
package com.example.thread;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
Thread thread = new Thread(myThread_3, "小明");
// 获取线程状态
System.out.println(thread.getState());
thread.start();
// 获取线程状态
System.out.println(thread.getState());
}
}
class MyThread_3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
}
}
}
结果

设置优先级
优先级从高到低:10到1 优先级越高,被优先执行的可能性越高,但是只是可能。 设置优先级,要在调用线程前设置,如果设置优先的范围超过1到10会报错package com.example.thread;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
Thread thread = new Thread(myThread_3, "小明");
Thread thread_1 = new Thread(myThread_3, "小红");
Thread thread_2 = new Thread(myThread_3, "小方");
thread_2.setPriority(Thread.MAX_PRIORITY);
thread_1.setPriority(Thread.MIN_PRIORITY);
thread.start();
thread_1.start();
thread_2.start();
}
}
class MyThread_3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
}
}
}
结果

4、线程的同步
解决多个线程访问同一个资源,出现的问题
问题
package com.example.thread;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
new Thread(myThread_3, "小明").start();
new Thread(myThread_3, "小红").start();
new Thread(myThread_3, "小方").start();
}
}
class MyThread_3 implements Runnable {
private int ticket = 10;
@Override
public void run() {
while (true) {
if (ticket <= 0) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程" + Thread.currentThread().getName() + ticket--);
}
}
}

方法一:使用synchronized代码块
package com.example.thread;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
new Thread(myThread_3, "小明").start();
new Thread(myThread_3, "小红").start();
new Thread(myThread_3, "小方").start();
}
}
class MyThread_3 implements Runnable {
private int ticket = 10;
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticket <= 0) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程" + Thread.currentThread().getName() + ticket--);
}
}
}
}

方法二:使用synchronized方法
package com.example.thread;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
new Thread(myThread_3, "小明").start();
new Thread(myThread_3, "小红").start();
new Thread(myThread_3, "小方").start();
}
}
class MyThread_3 implements Runnable {
private int ticket = 10;
private boolean flag = true;
@Override
public void run() {
while (flag) {
buy();
}
}
public synchronized void buy() {
if (ticket <= 0) {
flag = false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程" + Thread.currentThread().getName() + ticket--);
}
}

方法三:使用锁
package com.example.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02 {
public static void main(String[] args) {
MyThread_3 myThread_3 = new MyThread_3();
new Thread(myThread_3, "小明").start();
new Thread(myThread_3, "小红").start();
new Thread(myThread_3, "小方").start();
}
}
class MyThread_3 implements Runnable {
private int ticket = 10;
private static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if (ticket <= 0) {
break;
}
Thread.sleep(100);
System.out.println("当前线程" + Thread.currentThread().getName() + ticket--);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}

5、线程间的通信
wait()、notify()、和notifyAll()
wait()、notify()和notifyAll()是Object类中的方法
wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写,调用方法时,当前线程必须拥有对象的monitor(即锁),所以必须在同步块或者同步方法中进行。
调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor,调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但并不释放锁)。
调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程。当有多个线程都在等待该对象的monitor时,只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。
调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程。
notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。
生产者和消费者问题
方法一:管程法(使用容器)
生产者生产了10个商品,等待消费者消费后,才能生产,消费者消费了10个商品,等待生产者生产后在能消费
package com.example.thread;
public class Demo03 {
public static void main(String[] args) {
// 设置商店
Shop shop = new Shop();
// 设置消费者
new Consumer(shop).start();
// 设置生产者
new Provider(shop).start();
}
}
// 定义商品
class Chicken {
// 定义商品的id
int id;
public Chicken(int id) {
this.id = id;
}
}
// 定义商店
class Shop {
// 定义容器
Chicken[] chickens = new Chicken[10];
// 商品的个数
int count = 0;
// 生产商品
public synchronized void push(Chicken chicken) {
// 如果生产了10只商品,等待消费者消费
if (count == chickens.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 把生产的商品放入容器
chickens[count] = chicken;
// 商品个数+1
count++;
System.out.println("生产了第" + chicken.id + "个商品");
// 通知消费者已生产
this.notifyAll();
}
// 消费商品
public synchronized void pop() {
// 如果商品个数为0,等待生产者生产
if (count == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 数组是从0开始
count--;
// 获取商品
Chicken chicken = chickens[count];
System.out.println("消费了第" + chicken.id + "个商品");
// 通知生产已消费
this.notifyAll();
}
}
// 定义生产者
class Provider extends Thread {
private Shop shop;
public Provider(Shop shop) {
this.shop = shop;
}
@Override
public void run() {
// 生产商品
for (int i = 1; i <= 20; i++) {
shop.push(new Chicken(i));
}
}
}
// 定义消费者
class Consumer extends Thread {
private Shop shop;
public Consumer(Shop shop) {
this.shop = shop;
}
@Override
public void run() {
// 消费商品
for (int i = 0; i < 11; i++) {
shop.pop();
}
}
}

方法二:信号灯(使用标志位)
生产者生产时,消费者等待,消费者消费时,生产者等待
package com.example.thread;
public class Demo03 {
public static void main(String[] args) {
// 设置商店
Shop shop = new Shop();
// 设置消费者
new Consumer(shop).start();
// 设置生产者
new Provider(shop).start();
}
}
// 定义商店
class Shop {
// 信号灯
boolean flag = true;
// 产品名称
String goodsName;
// 生产商品
public synchronized void push(String goodsName) {
// 消费者消费时等待
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("生产商品的名称:" + goodsName);
// 设置商品名称
this.goodsName = goodsName;
// 生产完等待消费者消费
flag = !flag;
this.notifyAll();
}
// 消费商品
public synchronized void pop() {
// 生产者生产时等待
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费商品的名称:" + goodsName);
// 消费者等待生产者生产
flag = !flag;
this.notifyAll();
}
}
// 定义生产者
class Provider extends Thread {
private Shop shop;
public Provider(Shop shop) {
this.shop = shop;
}
@Override
public void run() {
// 生产商品
for (int i = 0; i < 15; i++) {
if (i % 2 == 0) {
shop.push("烤鸡");
} else {
shop.push("汉堡");
}
}
}
}
// 定义消费者
class Consumer extends Thread {
private Shop shop;
public Consumer(Shop shop) {
this.shop = shop;
}
@Override
public void run() {
// 消费商品
for (int i = 0; i < 15; i++) {
shop.pop();
}
}
}

浙公网安备 33010602011771号