多线程(Java.Thread)学习
线程简介:
1、线程是独立执行的路径
2、程序运行时有很多后台进程 比如主线程、young.gc、full.gc()
3、main是主线程,系统入口,用于执行整个程序
4、一个进程中、如果开辟多个线程,线程的运行由调度器安排调度、调度器的先后顺序不能人为干预
5、对同一份资源操作时,会存在资源抢夺问题,需要加入并发控制
6、线程会带来额外的开销,比如cpu调度时间,并发控制开销
7、每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程实现:
Thread
/**
* @Description:TODO
* @Author:Administrator
* @Create 2024/1/12
*/
public class ThreadTest extends Thread{
// 线程入口点
@Override
public void run() {
// 线程体
for (int i = 0;i<10;i++){
System.out.println("我在看蒂法....."+i);
}
}
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();//开启线程,不一定立即执行 需要cpu调度
threadTest.run();//立即执行线程
for (int i =0;i<10;i++){
System.out.println("我在学Java Thread"+i);
}
}
}
开启 Thread 线程 下载网图,验证start方法线程执行交给cpu。
public class ThreadTest2 extends Thread {
private String url;
private String name;
public ThreadTest2(String url,String name){
this.url = url;
this.name =name;
}
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.download(url,name);
System.out.println("下载了文件名为:"+name);
}
public static void main(String[] args) {
ThreadTest2 threadTest1 = new ThreadTest2("https://aistyle.art/assets/files/2023-06-30/1688166112-995353-348703237-145251275241612-1956428239713994473-n.jpg","明日香1.jpg");
ThreadTest2 threadTest2 = new ThreadTest2("https://bkimg.cdn.bcebos.com/pic/8b13632762d0f703918fecd9c0b1463d269759ee6f28?x-bce-process=image/format,f_auto/resize,m_lfit,limit_1,h_300","明日香2.jpg");
ThreadTest2 threadTest3 = new ThreadTest2("https://aistyle.art/assets/files/2023-06-30/1688166112-995353-348703237-145251275241612-1956428239713994473-n.jpg","明日香3.jpg");
threadTest1.start();
threadTest2.start();
threadTest3.start();
}
}
class WebDownloader{
//
public void download(String url,String name){
try{
FileUtils.copyURLToFile(new URL(url),new File(name));
System.out.println("下载完成");
}catch (IOException e){
e.printStackTrace();
}
}
}
实现runable接口
public class TestThread3 implements Runnable {
public static void main(String[] args) {
TestThread3 testThread3 = new TestThread3();
new Thread(testThread3).run();
for (int i = 0; i < 10; i++) {
System.out.println("我在学习" + i);
}
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我在聊天" + i);
}
}
}
初识并发问题
package org.example.redisson.JavaThread;
/**
* @Description:TODO
* @Author:Administrator
* @Create 2024/1/12
*/
/**
* 创建线程方式2:实现runable 接口重写run
* 执行线程需要啊丢入new Thread(TestThread3)
* 官方推荐 实现runable接口的方式
*/
public class TestThread4 implements Runnable {
// 模拟票数
private int ticketNums = 100;
@Override
public void run() {
while (true){
// 票剩余0时退出
if (ticketNums <= 0){
break;
}
// 模拟延时
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 对票数加锁
synchronized ((Object)ticketNums){
// 再次判断
if (ticketNums <= 0){
break;
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
}
}
}
public static void main(String[] args) {
TestThread4 ticket = new TestThread4();
new Thread(ticket,"陈X华").start();
new Thread(ticket,"席X辉").start();
new Thread(ticket,"李X杰").start();
}
}
龟兔赛跑
public class TestThread5 implements Runnable{
private static String Winer;
// 比赛内容
public void run(){
// 如果线程是兔子 让他休息20ms
if(Thread.currentThread().getName().equals("兔子")){
try{
Thread.sleep(20);
}catch(RuntimeException | InterruptedException e){
e.printStackTrace();
}
}
for(int i = 1;i<= 10;i++){
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
// 判断比赛是否结束
Boolean flage = gameOver(i);
if (flage){
break;
}
}
}
public static void main(String[] args){
TestThread5 testThread5 = new TestThread5();
new Thread(testThread5,"兔子").start();
new Thread(testThread5,"乌龟").start();
}
public Boolean gameOver(int step){
// 胜利者诞生 停止比赛
if(Winer != null){
return true;
}
// 跑过了10 步 停止比赛
if(step == 10){
Winer = Thread.currentThread().getName();
System.out.println("胜利者是"+Winer);
return true;
}
return false;
}
}
callable
函数式接口 lambda
JDK8 之前的写法
public class TestLambda2{
// 一个接口只有一个实现方法:叫做函数式接口
interface ILove{
void love(int a);
}
public static void main(String[] args){
ILove i = new I();
i.love(521);
}
class I implements ILove{
@Override
public void love(int a){
System.out.println("i love you " + a);
}
}
}
简化1:使用静态内部类
# 使用静态内部类
public class TestLambda2{
// 一个接口只有一个实现方法:叫做函数式接口
interface ILove{
void love(int a);
}
public static void main(String[] args){
ILove i = new I();
i.love(521);
}
static class I implements ILove{
@Override
public void love(int a){
System.out.println("i love you " + a);
}
}
}
简化2:局部内部类
public class TestLambda2{
// 一个接口只有一个实现方法:叫做函数式接口
interface ILove{
void love(int a);
}
public static void main(String[] args){
class I implements ILove{
@Override
public void love(int a){
System.out.println("i love you " + a);
}
}
ILove i = new I();
i.love(521);
}
}
简化3:匿名内部类(通过new 构建 没有方法名)
interface ILove{
void love(int a);
}
public class TestLambda2{
// 一个接口只有一个实现方法:叫做函数式接口
public static void main(String[] args){
ILove i = new ILove(){
@Override
public void love(int a){
System.out.print("i love you " + a)
}
};
i.love(521);
}
}
最终lambda版本
interface ILove {
void love(int a,int b);
}
public class TestLambda2 {
// 一个接口只有一个实现方法:叫做函数式接口
public static void main(String[] args) {
ILove i = (a,b) -> {
System.out.println("i love you " + a+b);
};
i.love(520,521);
}
}
Lambda总结
1、接口中只能有一个方法;
2、lambda执行业务逻辑时 必须带{},除非业务逻辑只有一行
3、多个参数 也可以去掉参数类型(全部去掉)
线程状态:
创建 new Thread()
就绪(等待cpu调度) start();
阻塞(阻塞结束后 等待cpu调度) sleep()、wait()或同步锁
sleep
sleep(ms) 指定当前线程阻塞的毫秒数;
sleep 存在异常InterruptedException;
sleep 时间到达后线程进入就绪状态;
sleep 可以模拟网络延时。倒计时等
每一个对象都有一把锁。sleep不会释放锁;
/**
* @Description: 联系sleep 倒计时
* @Author:Administrator
* @Create 2024/1/13
*/
public class TestThreadSleep{
public static void main(String[] args) throws InterruptedException {
tenShutDown();
}
static void tenShutDown() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println( num-- );
if (num <= 0){
break;
}
}
}
}
join 使线程阻塞,相等于vip先执行
/**
* @Description:测试 join,会使线程阻塞
* @Author:Administrator
* @Create 2024/1/13
*/
public class TestThreadJoin implements Runnable{
@Override
public void run() {
for (int i = 1; i < 101; i++) {
System.out.println("vip来了 统统闪开~ !"+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestThreadJoin testThreadJoin = new TestThreadJoin();
Thread thread = new Thread(testThreadJoin);
thread.start();
for (int i = 1; i < 101; i++) {
System.out.println("main"+ i);
if(i == 10){
thread.join();// 强制执行 run方法 线程
}
}
}
}
运行
start()、run()
销毁
不建议用stop、die废弃或官方不推荐的方法
- 推荐基数的方式 停止线程
/*
* @Description:不建议用stop、die废弃或官方不推荐的方法
* 推荐基数的方式 停止线程
* @Author:Administrator
* @Create 2024/1/13
*/
public class TestThreadStop implements Runnable {
public Boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("走了" + i++ + "步数");
}
}
// 设置一个公开的停止方法
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
TestThreadStop testThreadStop = new TestThreadStop();
new Thread(testThreadStop).start();
for (int i = 0; i < 100; i++) {
System.out.println("main"+ i);
if (i == 90) {
// 循环90次的时候 停止线程
testThreadStop.stop();
System.out.println("线程该停止了");
}
}
}
}
观察线程的状态
启动前:new
启动后:runnable
运行:Timed-waiting
结束: TERMINATED
线程的优先级
优先级:1-10;数字越大优先级越高;
守护线程
1、通过Thread.seDeamon(true) 设置
2、在线程start 前设置
3、用户线程挂掉,守护线程也会等待cpu调度挂掉
线程同步机制
多个线程同时对公共对象操作,就是并发问题
如何解决并发安全问题
1、使用线程同步队
sync 同步锁:
一个线程持有锁 会导致其他所有需要次锁的线程挂起
一定会损失性能
优先级高的线程,等待优先级低的线程 释放锁会有性能问题 鱼和熊掌不可兼得
三大不安全案例
1、两人同时对同一张银行卡取钱(超取问题)
2、网上买票(超卖问题)
3、1000个线程对ArrayList同时操作,数据不一致(array.add方法是根据下标位置,多个线程可能会同时锁定下标位置)
同步方法及同步块
sync
CopyOnWriteArrayList
JUC Java并发编程
java util.concurren.copyOnWirteArrayList;线程安全的数据类型
死锁
a 想占用 b的资源
b 想占用 a的资源
都在等对方释放
Lock锁
ReentrantLock实现了Lock 是可重入锁
copyOnWirteArrayList 中使用了;
Sync和Lock的区别
Lock是显式锁 需要手动开启、关闭,sync是隐式锁 出了区域自动释放
Lock只有代码块锁,sync有代码块锁也有方法锁
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好
生产者消费问题(线程协作)
生产者消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取出消费
如果仓库中没有产品,则生产者将产品放入仓库,否则停止并等待并等待仓库中的产品被消费者取走为止
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止生产并等待,直到仓库中再次放入产品为止
管程法
信号灯法
设置flag信号灯管理 生产消费的通信 flag为true消费,否则生产
线程池
Executor