线程 Thread
线程
普通方法调用和多线程
Process和Thread
- 程序是指令和数据的有序集合,是静态概念
- 进程Process是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位
- 通常一个进程中可以包含多个线程,每个进程中至少有一个线程,线程是CPU调度和执行的单位
核心概念
- 线程是独立的执行路径
- 程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程(清理)
- main()称为主线程,是系统的入口,用于执行整个程序
- 一个进程中,如开辟了多个线程,线程的运行由调度器安排
- 对一份资源进行操作时,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如CPU调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程创建
Thread类(重点)
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法开启线程
package OOP.Demo;
/**
* @version: java version 1.8
* @Author: 14
*/
public class Thread01 extends Thread{
//创建线程方式一: 继承Thread类,重写run方法,调用start开启线程
//总结:线程开启不一定立即执行,由CPU调度
@Override
public void run() {//重写run方法
for (int i = 0; i <500 ; i++) {
System.out.println("这里是线程01");
}
}
public static void main(String[] args) {//main线程,主线程
//创建线程对象
Thread01 t1 = new Thread01();
//调用start();开启线程
t1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("这里是main");
}
}
}
例子:下载图片测试
package OOP.Demo;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* @version: java version 1.8
* @Author: 14
*/
public class Thread02 extends Thread{
private String Url;
private String path;
public Thread02(String Url,String path){
this.Url = Url;
this.path = path;
}
@Override
public void run() {
new WebDownloader().downloader(Url,path);
// WebDownloader webDownloader = new WebDownloader();
// webDownloader.downloader(Url,path);
System.out.println("下载了文件在:"+path);
}
public static void main(String[] args) {
Thread02 t1 = new Thread02("https://img2024.cnblogs.com/blog/3475380/202407/3475380-20240701154652288-1747217106.png","1.png");
Thread02 t2 = new Thread02("https://img2024.cnblogs.com/blog/3475380/202407/3475380-20240701154705043-664732010.png","2.png");
Thread02 t3 = new Thread02("https://img2024.cnblogs.com/blog/3475380/202407/3475380-20240701154710607-423679454.png","3.png");
t1.start();
t2.start();
t3.start();
}
}
class WebDownloader{
public void downloader(String url,String path){
try {
FileUtils.copyURLToFile(new URL(url),new File(path));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IOException");
}
}
}
Runnable接口(重点)
-
定义MyRunnable类实现Runnable接口
-
实现run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
推荐使用Runnable对象,因为Java单继承的局限性
package OOP.Demo;
/**
* @version: java version 1.8
* @Author: 14
*/
public class Thread03 implements Runnable{
@Override
public void run() {
for (int i = 0; i <500 ; i++) {
System.out.println("这里是线程01");
}
}
public static void main(String[] args) {//main线程,主线程
//创建线程对象
Thread03 t1 = new Thread03();
//调用start();开启线程
new Thread(t1).start();//区别在这
for (int i = 0; i < 1000; i++) {
System.out.println("这里是main");
}
}
}
例子:购买火车票
package OOP.Demo;
/**
* @version: java version 1.8
* @Author: 14
*/
//多个线程同时操作一个对象
//买火车票例
//发现问题:多个线程操作同一个资源,线程不安全,数据紊乱
public class Thread04 implements Runnable{
//票数
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if(ticketNums>0){
System.out.println(Thread.currentThread().getName()+"->拿到了第"+ticketNums--+"票");
}else {
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread04 t4 = new Thread04();
new Thread(t4,"小红").start();
new Thread(t4,"小米").start();
new Thread(t4,"万恶的黄牛").start();
}
}
例子:龟兔赛跑
package OOP.Demo;
/**
* @version: java version 1.8
* @Author: 14
*/
public class Race implements Runnable{
static String winner = null;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (Thread.currentThread().getName().equals("兔子") && i%11 == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (winner(i)){
break;
}
System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
}
}
public boolean winner(int steps){
if(winner != null){
return true;
}
if(steps >= 100){
winner = Thread.currentThread().getName();
System.out.println("胜者是:" + winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
Callable接口
-
实现Callable接口,需要返回值类型
-
重写call()方法,需要抛出异常
-
创建目标对象
-
创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(1);
-
提交执行:
Future<Boolean> result = ser.submit(t1);
-
获取结果:
boolean r = result.get();
-
关闭服务:
ser.shutdownNow();
例子:利用Callable改造下载图片案例
package OOP.Demo;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
/**
* @version: java version 1.8
* @Author: 14
*/
public class Thread05 implements Callable<Integer> {
private String Url;
private String path;
public Thread05(String Url,String path){
this.Url = Url;
this.path = path;
}
@Override
public Integer call() {
new WebDownloader().downloader(Url,path);
// WebDownloader webDownloader = new WebDownloader();
// webDownloader.downloader(Url,path);
System.out.println("下载了文件在:"+path);
return 0;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread05 t1 = new Thread05("https://img2024.cnblogs.com/blog/3475380/202407/3475380-20240701154652288-1747217106.png","1.png");
Thread05 t2 = new Thread05("https://img2024.cnblogs.com/blog/3475380/202407/3475380-20240701154705043-664732010.png","2.png");
Thread05 t3 = new Thread05("https://img2024.cnblogs.com/blog/3475380/202407/3475380-20240701154710607-423679454.png","3.png");
// t1.start();
// t2.start();
// t3.start();
//创建服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Integer> f1 = ser.submit(t1);
Future<Integer> f2 = ser.submit(t2);
Future<Integer> f3 = ser.submit(t3);
//获取结果
Integer r1 = f1.get();
Integer r2 = f2.get();
Integer r3 = f3.get();
//打印结果
System.out.println(r1);
System.out.println(r2);
System.out.println(r3);
//关闭服务
ser.shutdownNow();
}
}
//class WebDownloader{
// public void downloader(String url,String path){
// try {
// FileUtils.copyURLToFile(new URL(url),new File(path));
// } catch (IOException e) {
// e.printStackTrace();
// System.out.println("IOException");
// }
// }
//}
Lambda表达式
-
函数式接口:
任何接口,如果只包含唯一的一个抽象方法,那么它就是函数式接口
public interface Runnable{ public abstract void run(); }
-
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
package OOP.Demo;
/**
* @version: java version 1.8
* @Author: 14
*/
public class LambdaTest {
//3、静态内部类
static class ILove1 implements Love{
@Override
public void love() {
System.out.println("I LOVE -->1");
}
}
public static void main(String[] args) {
//4、局部内部类
class ILove2 implements Love{
@Override
public void love() {
System.out.println("I LOVE -->2");
}
}
ILove iLove3 = new ILove();
//5、匿名内部类
iLove3 = new ILove(){
@Override
public void love() {
System.out.println("I LOVE -->3");
}
};
//6、Lambda表达式
Love iLove4 = ()-> { System.out.println("I LOVE -->4"); };
ILove iLove = new ILove();
ILove1 iLove1 = new ILove1();
ILove2 iLove2 = new ILove2();
iLove.love();
iLove1.love();
iLove2.love();
iLove3.love();
iLove4.love();
}
}
//1、定义函数式接口
interface Love {
void love();
}
//2、实现类
class ILove implements Love{
@Override
public void love() {
System.out.println("I LOVE -->0");
}
}
Lambda简化
总结
- lambda表达式只有一行代码的情况下,才能简化为一行,如有多行,需要花括号包裹
- 使用lambda表达式的前提是 接口为函数式接口
- 多个参数也可以去掉参数类型(要去就都去掉),用括号包裹
package OOP.Demo;
/**
* @version: java version 1.8
* @Author: 14
*/
public class LambdaTest {
public static void main(String[] args) {
//Lambda表达式
Love iLove = ()-> { System.out.println("I LOVE -->0"); };
//Lambda简化
Love1 iLove1 = a -> System.out.println("I LOVE -->" + a);
Love2 iLove2 = (a,b) -> {
System.out.println("I LOVE -->" + a);
System.out.println("I LOVE -->" + b);
};
iLove.love();
iLove1.love1(1);
iLove2.love2(2,3);
}
}
//1、定义函数式接口
interface Love {
void love();
}
interface Love1{
void love1(int a);
}
interface Love2{
void love2(int a, int b);
}
静态代理模式(线程的底部原理)
总结:
-
真实对象和代理对象都要实现同一个接口
-
代理对象必须要代理真实对象
好处:
-
代理对象可以做很多真实对象做不了的事情
-
真实对象专注做自己的事情
package OOP.Demo;
/**
* @version: java version 1.8
* @Author: 14
*/
public class StaticProxy {
public static void main(String[] args) {
Person person = new Person();
MarryCompany marryCompany = new MarryCompany(person);
marryCompany.marry();
//线程启动,也相当与静态代理
new Thread(()-> System.out.println("这是一个线程")).start();
new MarryCompany(new Person()).marry();
}
}
interface Marry{
void marry();
}
class Person implements Marry{
@Override
public void marry() {
System.out.println("找到漂亮老婆结婚啦!!!");
}
}
class MarryCompany implements Marry{
Person person;
public MarryCompany(Person person) {
this.person = person;
}
@Override
public void marry() {
before();
person.marry();
after();
}
public void before(){
System.out.println("婚庆人员在布置现场");
}
public void after(){
System.out.println("婚礼结束了,打钱");
}
}
线程五大状态
- 创建状态
- 就绪状态
- 运行状态
- 阻塞状态
- 死亡状态
停止线程
- 不推荐使用JDK提供的stop()、destroy()方法。(已废弃)
- 推荐线程自己停下来
- 使用一个标志位当作终止变量:当flag = false,终止线程运行
package OOP.Demo01;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestStop implements Runnable {
//设置标志位
private boolean flag = true;
@Override
public void run() {
int i = 1;
while (flag) System.out.println("Thread is running "+ i++);
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main thread is running "+ i);
if ( i == 900) {
//调用stop()方法切换标志位,停止线程
testStop.stop();
System.out.println("Thread is stopped");
}
}
}
//设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
}
线程休眠 Sleep
- sleep指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException;需要抛出
- sleep时间达到后进入就绪状态
- sleep可以模拟网络时延,倒计时等
- 每个对象都有一个锁,sleep不会释放锁
package OOP.Demo01;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @version: java version 1.8
* @Author: 14
*/
//倒计时/输出系统时间
public class TestSleep01 {
public static void main(String[] args) {
// try {
// tenDown();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
Date date = new Date(System.currentTimeMillis());//获取当前系统时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));//格式化时间
date = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void tenDown() throws InterruptedException {
int i = 10;
while (true){
if (i > 0){
Thread.sleep(1000);
System.out.println(i--);
}else {
break;
}
}
}
}
线程礼让 Yield
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 让线程从运行状态转为就绪状态
- 礼让不一定成功,看CPU重新调度
package OOP.Demo01;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println("Thread "+ Thread.currentThread().getName()+" Start");
Thread.yield();
System.out.println("Thread "+ Thread.currentThread().getName()+" End");
}
}
线程强制执行 Join
- Join合并线程,待此线程执行完后再执行其他线程,其他线程阻塞
- 相当于插队
package OOP.Demo01;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestJoin implements Runnable{
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
System.out.println("大小姐驾到,统统闪开" + i );
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin t = new TestJoin();
Thread t1 = new Thread(t);
t1.start();
for (int i = 0; i < 500; i++) {
if (i == 200) t1.join();
System.out.println("主线程在执行" + i );
}
}
}
观测线程状态 State
package OOP.Demo01;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---------------------");
});
Thread.State state = thread.getState();
System.out.println(state);//NEW
//开启线程
thread.start();
state = thread.getState();
System.out.println(state);//RUN
while (state != Thread.State.TERMINATED) {
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
线程优先级 Priority
-
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
-
线程的优先级用数字表示,范围 1-10
- Thread.MIN_PRIORITY = 1
- Thread.NORM_PRIORITY = 5
- Thread.MAX_PRIORITY = 10
-
使用以下方法改变和获取优先级
- setPriority(int xxx)
- getPriority()
优先级的设定一般放在start()调度前
守护线程 Deamon
-
线程分为用户线程和守护线程
-
虚拟机必须确保用户线程执行完毕
-
虚拟机不用等待守护线程执行完毕
如:后台记录操作日志、监控内存、垃圾回收等待
package OOP.Demo01;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestDeamon {
public static void main(String[] args) {
God god = new God();
Person person = new Person();
Thread t1 = new Thread(god);
t1.setDaemon(true);//默认为false,用户线程
t1.start();
new Thread(person).start();
}
}
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("God bless you");
}
}
}
class Person implements Runnable{
@Override
public void run() {
System.out.println("----I Was Born----");
for (int i = 0; i < 36500; i++) {
System.out.println("I have a happy day");
}
System.out.println("----Goodbye My Love----");
}
}
线程同步
- 现实生活中,都会遇到“同一个资源,多个人想使用”的问题,解决办法是排队,一个一个来
- 处理多线程问题,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个 对象的等待池 形成队列,等待前面线程使用完毕,下一个线程再使用
队列和锁
线程同步形成条件:队列+锁
-
为了保证数据在方法中被访问时的正确性,在访问时加入锁机制 synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。
但是会存在以下问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 在多线程的竞争下,加锁,释放锁会导致比较多的上下文切换 和 调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
-
由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,synchronized关键字:synchronized方法和synchronized块
//同步方法 public synchronized void methon(int args){}
synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞。方法一旦执行,就独占该锁,直到该方法返回,后面被阻塞的线程才能获得这个锁,继续执行。
缺陷:若将一个大的方法声明为synchronized将影响效率
同步块:synchronized(obj){ }
- obj被称为同步监视器
- obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class
- 同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中的代码
- 第二个线程访问,发现同步监视器别锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器
- 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
- obj被称为同步监视器
-
方法里需要修改的内容才加锁,锁得太多,浪费资源
例子:取钱
package OOP.syn;
/**
* @version: java version 1.8
* @Author: 14
*/
public class UnsafeBank {
public static void main(String[] args) throws InterruptedException {
Account account = new Account("零花钱",1000);
new Drawing(account,300,"小花").start();
new Drawing(account,800,"小草").start();
}
}
class Account {
private String id;
private int balance ;
public Account(String id, int balance) {
this.id = id;
this.balance = balance;
}
public String getId() {
return id;
}
public int getBalance(){
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
public void setId(String id) {
this.id = id;
}
}
class Drawing extends Thread {
private Account account;//账户
int drawingMoney;//要取的钱
int nowMoney;//现在的钱
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱
@Override
public void run() {
//synchronized块,锁了()里的
synchronized (account) {
//判断有没有钱
if (account.getBalance() - drawingMoney < 0){
System.out.println(Thread.currentThread().getName()+ "余额不足" );
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//卡内余额
account.setBalance(account.getBalance() - drawingMoney);
System.out.println("当前余额:"+account.getBalance());
//手里的钱
nowMoney += drawingMoney;
System.out.println(this.getName()+"手里的钱:"+nowMoney);
}
}
}
例子:买票
package OOP.syn;
/**
* @version: java version 1.8
* @Author: 14
*/
//线程不安全,有重复
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"小红").start();
new Thread(buyTicket,"黄牛").start();
new Thread(buyTicket,"大白").start();
}
}
class BuyTicket implements Runnable{
//票
private int tickets = 10;
//停止标准
boolean flag = true;
@Override
public void run() {
while(flag){
try {
Thread.sleep(100);
buy();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
//synchronized 同步方法,锁this
private synchronized void buy(){
//判断是否有票
if(tickets <= 0){
flag = false;
return;
}
//买票
System.out.println(Thread.currentThread().getName() + "拿到" + tickets--);
}
}
例子:List和CopyOnWriteArrayList
package OOP.syn;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @version: java version 1.8
* @Author: 14
*/
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> list = new ArrayList<String>();
CopyOnWriteArrayList<String> list1 = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
//synchronized块 锁list
synchronized (list) {
list.add(Thread.currentThread().getName());
}
list1.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
System.out.println(list1.size());
}
}
死锁
- 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题
package OOP.syn;
/**
* @version: java version 1.8
* @Author: 14
*/
public class DeadLock {
public static void main(String[] args) {
MakeUp g1 = new MakeUp(MakeUp.lipstick,MakeUp.mirror,"花姑娘",0);
MakeUp g2 = new MakeUp(MakeUp.lipstick,MakeUp.mirror,"黑姑娘",1);
g1.start();
g2.start();
}
}
class Lipstick{
}
class Mirror{
}
class MakeUp extends Thread{
//需要的资源只有一份,用static
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
//选择
int choice = 0 ;
//使用化妆品的人
String name ;
MakeUp(Lipstick lipstick , Mirror mirror ,String name,int choice){
super(name);
this.lipstick = lipstick;
this.mirror = mirror;
this.choice = choice;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//化妆,互相持有对方的锁,拿到对方的资源
private void makeup() throws InterruptedException {
if (choice == 0){
synchronized (mirror){
System.out.println(this.getName() + "获得mirror的锁");
sleep(1000);
synchronized (lipstick){
System.out.println(this.getName() + "获得lipstick的锁");
}
}
} else {
synchronized (lipstick){
System.out.println(this.getName() + "获得lipstick的锁");
sleep(1000);
synchronized (mirror){
System.out.println(this.getName() + "获得mirror的锁");
}
}
}
}
//破除死锁
// private void makeup() throws InterruptedException {
// if (choice == 0){
// synchronized (mirror){
// System.out.println(this.getName() + "获得mirror的锁");
// sleep(1000);
// }
// synchronized (lipstick){
// System.out.println(this.getName() + "获得lipstick的锁");
// }
// } else {
// synchronized (lipstick){
// System.out.println(this.getName() + "获得lipstick的锁");
// sleep(1000);
// }
// synchronized (mirror){
// System.out.println(this.getName() + "获得mirror的锁");
// }
// }
// }
}
-
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求并保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 非剥夺:进程已经获得的资源,在未使用完之前,不能强行剥夺
- 循环等待:若干进程之间形成一种头尾相接的循环等待关系
我们只要想办法破除其中任意一个或多个条件就能避免死锁
Lock锁
ReentrantLock 可重入锁
class A{
private final ReentrantLock lock = new ReentrantLock();
public void m(){
try{
lock.lock();
...
代码块
...
} finally{
lock.unlock();
//如果同步代码有异常,要将unlock()写入finally语句块
}
}
}
例子:买票
package OOP.syn;
import java.util.concurrent.locks.ReentrantLock;
/**
* @version: java version 1.8
* @Author: 14
*/
//线程不安全,有重复
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"小红").start();
new Thread(buyTicket,"黄牛").start();
new Thread(buyTicket,"大白").start();
}
}
class BuyTicket implements Runnable{
//票
private int tickets = 10;
//停止标准
boolean flag = true;
//定义Lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(flag){
try {
lock.lock();
Thread.sleep(100);
buy();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}
}
//synchronized 同步方法,锁this
private void buy(){
//判断是否有票
if(tickets <= 0){
flag = false;
return;
}
//买票
System.out.println(Thread.currentThread().getName() + "拿到" + tickets--);
}
}
synchronized与Lock对比:
- Lock是显式锁(手动开启和关闭) synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的拓展性
- 优先使用顺序:
- Lock > 同步代码块 > 同步方法
线程通信
生产者消费者问题
- 假设仓库能存放一些产品,生产者将生产出来的产品放入仓库,消费者从仓库中取走产品消费
- 如果仓库中产品没有满,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中库存不满
- 如果仓库中有库存,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中有库存
这是一个线程同步问题,生产者消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。
解决方法一
并发协助模型“生产者/消费者模式” --->管程法
- 生产者:负责生产数据的模块(可能是方法、对象、线程、进程)
- 消费者:负责处理数据的模块(可能是方法、对象、线程、进程)
- 缓冲区:消费者不能直接使用生产者的数据,它们之间有个缓冲区(生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据)
package OOP.syn;
/**
* @version: java version 1.8
* @Author: 14
*/
//测试:生产者消费者模型 --> 利用缓冲区解决:管程法
//生产者、消费者、产品、缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
Producter producter = new Producter(synContainer);
Consumer consumer = new Consumer(synContainer);
new Thread(producter).start();
new Thread(consumer).start();
}
}
//生产者
class Producter implements Runnable{
SynContainer container;
public Producter(SynContainer container){
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
try {
System.out.println("生产了--->第" + i + "只鸡");
container.push(new Chicken(i));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//消费者
class Consumer implements Runnable{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
try {
System.out.println("消费了--->第" + container.pop().id + "只鸡");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//产品
class Chicken{
int id ;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer {
//创建容器
static Chicken[] chickens = new Chicken[10];
//计数器
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken) throws InterruptedException {
//如果容器满了,等待消费者消费
if (count == chickens.length) {
//通知消费者消费
this.wait();
}
//没满,放入产品
chickens[count] = chicken;
count++;
//通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop() throws InterruptedException {
//如果没有产品,等待生产者生产
if (count == 0) {
this.wait();
}
//有库存,消费产品
count--;
Chicken chicken = chickens[count];
//通知生产者生产
this.notifyAll();
return chicken;
}
}
解决方法二
并发协助模型“生产者/消费者模式” --->信号灯法
package OOP.syn;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestPC2 {
public static void main(String[] args) {
Pie pie = new Pie();
Producter1 producter1 = new Producter1(pie);
Consumer1 consumer1 = new Consumer1(pie);
new Thread(producter1).start();
new Thread(consumer1).start();
}
}
class Producter1 implements Runnable{
Pie pie ;
public Producter1(Pie pie) {
this.pie = pie;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
switch (i%3){
case 0:
pie.product("菠萝");
break;
case 1:
pie.product("苹果");
break;
case 2:
pie.product("香蕉");
break;
}
}
}
}
class Consumer1 implements Runnable{
Pie pie ;
public Consumer1(Pie pie) {
this.pie = pie;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
pie.consume();
}
}
}
class Pie{
String name;
boolean flag = true;
//生产
public synchronized void product(String name){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("制作了一个" + name + "派");
this.name = name;
this.flag = !this.flag;
this.notifyAll();
}
//消费
public synchronized void consume(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("购买了一个" + name + "派");
this.flag = !this.flag;
this.notifyAll();
}
}
线程池
-
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能的影响很大
-
思路:提前创建好对各线程,放入线程池中,使用时直接获取,使用完后放回池中。
可以避免频繁的创建销毁、实现重复利用 。类似公共交通工具
-
好处:
- 提高响应速度(减少了创建新线程的时间)
- 较低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理()
- coreRoolSize:核心池大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多存活时间
-
ExecutorService:真正的线程池接口
- void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
Future submit(Callable task):执行任务,有返回值,一般用来执行Callable - void shutdown():关闭线程池
-
Executors:工具类、线程池的工厂类,用于创建和返回不同类型的线程池
ExecutorService service = Executes.newFixedThreadPool(int nums/*要创建的线程数*/); service.execute(Thread/*要启动的线程*/); service.shutdown();//关闭
package OOP.syn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestPool {
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//启动线程
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭线程
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
package OOP.syn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestPool {
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//启动线程
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭线程
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
package OOP.syn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestPool {
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//启动线程
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭线程
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
package OOP.syn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @version: java version 1.8
* @Author: 14
*/
public class TestPool {
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//启动线程
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭线程
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}