Java学习笔记整理: 关于多线程完整版 2024/7/4;

常用方法

线程内自身方法:
Thread.currentThread().getName();获取当前线程对象/并获取当前对象名
Thread.sleep(20);延迟执行时间 单位毫秒
Thread.yield();收回当前执行线程,下次获取到线程后执行上次未完代码
线程通信:
wait();使当前线程进入等待状态
notify();随机解除一个等待状态线程
notifyAll();解除全部等待状态线程
线程安全:
synchronized(共享锁对象){代码块}共享锁:自动锁,执行完毕自动解锁;
private Lock l = new ReentrantLock();创建Lock手动锁;
l.lock();加锁;
l.unlock();解锁;
线程外调用方法:
a.start();启用a线程
a.join()将a线程拉回主线程执行
a.setDaemon(true)设置a为守护线程(守护的线程一旦结束那么守护线程也同时结束)

三种创建多线程的方法:

第一种: 继承Thread类
​
第二种: 实现Runnable接口
​
第三种: 实现Callable接口
  1. 多线程常用方法:

线程定义:

线程,又称轻量级进程(Light Weight Process) 线程是进程中的一条执行路径,也是CPU的基本调度单位。若一个程序可同一时间执行多个线程,就是支持多线程的. 一个进程由一个或多个线程组成,彼此间完成不同的工作(任务),同时执行,称为多线程。

为什么使用多线程:

多线程能提高cpu的利用率,提升运行效率;

创建多线程:

Java中提供了三种实现多线程的方式:

第一种: 继承Thread类

第二种: 实现Runnable接口

第三种: 实现Callable接口(有返回值)

Thread:
package com.gzx.thread;
​
//继承Thread类
public class MyThreadOne extends Thread{
​
    //重写run()方法
    @Override
    public void run() {
        System.out.println("线程1正在执行");
        
        //获取当前线程对象        获取对象名
        Thread.currentThread().getName();
        }
    }
}
package com.gzx;
​
import com.gzx.thread.MyThreadOne;
import com.gzx.thread.MyThreadTwo;
​
public class Main {
    public static void main(String[] args) {
        System.out.println("主线程启动");
        //引入线程对象
        MyThreadOne myThreadOne = new MyThreadOne();
        //启用该对象线程
        myThreadOne.start();
    }
}
Runnable:
package com.gzx.runnable;
​
//实现Runnable接口
public class MyRunnableOne implements Runnable{
    
    //重写run()方法
    @Override
    public void run() {
        System.out.println("MyRunnableOne is running");
    }
}
package com.gzx;
​
import com.gzx.runnable.MyRunnableOne;
​
public class MainTwo {
    public static void main(String[] args) {
        //引入线程对象
        MyRunnableOne myRunnableOne = new MyRunnableOne();
        //封装线程对象并命名
        Thread t1 = new Thread(myRunnableOne,"线程A");
        //开启该对象线程
        t1.start();
        
        System.out.println("主线程执行");
    }
​
}
Callable:
package com.gzx.callable;
​
import java.util.concurrent.Callable;
​
//实现Callable<类>接口
public class MyCallableOne implements Callable<Integer> {
    
    //重写call()方法
    @Override
    public Integer call() throws Exception {
        // 定义一个整数变量nums,初始值为0
        Integer nums = 0;
        // 使用for循环,循环条件为3*i<1000,循环变量i从0开始
        for (int i = 0; 3*i <= 1000; i++) {
            // 将3*i的值加到nums上
            nums+=3*i;
        }
        // 返回nums的值
        return nums;
    }
}
package com.gzx;
​
import com.gzx.callable.MyCallableOne;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
​
public class MainThree {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建多线程对象
        MyCallableOne myCallableOne = new MyCallableOne();
        //把call对象封装到FutureTask类中 
        //而且任务执行的结果也在FutureTask
        FutureTask task = new FutureTask(myCallableOne);
        Thread thread = new Thread(task);
        //开启该对象线程
        thread.start();
        //获取该对象返回值
        Object o = task.get();
        //输出
        System.out.println(o);
    }
}

线程安全:

线程安全引发原因:

当多个线程操作同一个资源时,则出现线程安全问题。

如何解决线程安全问题

使用锁相对于把原来的异步转换为同步操作(并行转换为串行)

(1):synchronized可以使用代码块和方法。自动加锁和释放锁。不会出现死锁问题。

(2):lock手动锁它只能使用在代码块中。需要手动加锁和释放锁。如果不释放锁,死锁问题。灵活。它的释放锁必须放在finally.

  1. 使用synchronized自动锁

    可用于方法或代码块中

    synchronized(共享锁对象){
     同步代码块。
    }
     public synchronized void sell(){
            if (tick > 0) {
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                tick--;
                System.out.println(Thread.currentThread().getName() + "卖了一张票;剩余:" + tick + "张");
            }
        }
  2. 使用Lock手动锁。

    Lock它是一个接口,它的实现类。ReentrantLock

    public class SellTicket implements Runnable {
        private int tick = 100;
    ​
        private Lock l = new ReentrantLock();
        
        @Override
        public void run() {
            while (true) {
                try {
                    l.lock();//加锁
                    if (tick > 0) {
                        try {
                            Thread.sleep(20);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
       System.out.println(Thread.currentThread().getName()+(--tick));
                    } else {
                        break;
                    }
                } finally {
                    l.unlock();//解锁
                }
    ​
            }
        }
    ​
    ​
    }
死锁:

线程A拥有锁a,希望获取锁b,线程B拥有锁b,希望获取锁a。 两个线程互相拥有对方希望获取的锁资源。可能会出现程序堵塞。从而造成死锁。

1. 不要使用锁嵌套。
2. 设置超时时间。--Lock类中tryLock.
3. 使用安全java.util.concurrent下的类。

线程通信:

wait方法:

wait方法(使当前线程进入等待状态)需要写入锁中

notify方法:

直接使用notify随机解除一个等待状态中的线程

使用notifyAll解除全部等待状态中的线程

public class MyRunnableOne implements Runnable{
    @Override
    public void run() {
        for (int j = 0; j < 10; j++) {
           if(Thread.currentThread().getName()=="线程A"){
               //调用进入等待状态方法
               waits();
           }else {
               //调用解除等待方法
               addMoney();
           }
        }
    }
    public synchronized void waits(){
        try {
            //使当前线程进入等待状态
            wait();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    public synchronized void addMoney(){
        notifyAll();//解除全部等待线程
    }
}

线程状态:

状态名通过调用不同的方法相互转换
NEW新建状态
RUNNABLE就绪状态和运行状态
BLOCKED堵塞状态
WAITING等待状态
TIMED_WAITING时间等待
TERMINATED终止

各方法调用均需要经过运行状态BUNNABLE

  • 47
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值