JAVA线程学习笔记

java线程

学完东西不进脑,那就记下来吧
更多的知识以后补充

一. 线程,进程,多线程

先说程序,程序 为指令与数据的有序集合

程序 执行一次就称为一个 进程

而一个 进程 中可以有若干个 线程

线程 是一个独立的执行路径

main()称为线程入口,用于执行整个程序

将一个进程交由多个线程执行,称为多线程

二. 线程创建三种方法

1.Thread class ( 继承Thread类 )

package Thread;

public class TestThread1 extends Thread{
    @Override
    public void run(){
        //run方法线程体
        for(int i=0;i<20;i++)
            System.out.println("魂--"+i);
    }

    //main线程---主线程体
    public static void main(String[] args){
        //先实例化一个线程对象
        TestThread1 testThread1 = new TestThread1();

        //调用start()方法开启线程
        //testThread1.start();
        testThread1.start();

        for(int i=0;i<20;i++)
            System.out.println("左殇--"+i);

    }
}

2.Runnable接口(实现Runnable接口)

package Thread;

//创建线程方法2
public class TestThread3 implements Runnable{
    @Override
    public void run(){
        //run方法线程体
        for(int i=0;i<20;i++)
            System.out.println("魂--"+i);
    }

    //main线程---主线程体
    public static void main(String[] args){
        //先实例化一个线程对象
        TestThread3 testThread3=new TestThread3();

        //创建线程对象,再通过线程对象开启线程(代理)
        //Thread thread = new Thread(testThread3);
        //thread.start();

        new Thread(testThread3).start();

        for(int i=0;i<20;i++)
            System.out.println("左殇--"+i);

    }

}

3.Callable接口(实现Callable接口)

三. 并发问题

多个线程对一个数据进行读写操作时引发问题


public class TestThread4 implements Runnable{

    private int ticketNums = 10;

    @Override
    public void run(){
        while(true){
            if (ticketNums==0)
                break;
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"黄贴");
        }
    }

    public static void main(String[] args) {
        TestThread4 ticket = new TestThread4();

        new Thread(ticket,"虚空").start();
        new Thread(ticket,"银月").start();
        new Thread(ticket,"邪后").start();
    }
}

四. 静态代理模式(线程代码逻辑层面的基本原理)

我们所创建的线程称为 真实对象,而 Thread 则称为一个 代理对象
我们重写的 Runnable 就是一个 接口


public class StaticProxy {
    public static void main(String[] args) {
        Thread_ thread_=new Thread_(new Thread_one());
        thread_.run_();
    }

}

interface Runnable_{
    public void run_();
}

class Thread_one implements Runnable_{
    @Override
    public void run_(){
        System.out.println("Do somethings");
    }
}

class Thread_ implements Runnable_{

    private Runnable_ target;

    public Thread_(Runnable_ target) {
        this.target = target;
    }

    @Override
    public void run_(){
        before();
        this.target.run_();
        after();
    }

    private void before() {
        System.out.println("Prepare");
    }

    private void after() {
        System.out.println("Finish");
    }
}

五. Lamda表达式

一个接口类里只有一个方法,称为函数式接口
这种函数式接口可以用lambda表达式替代

我们常常会碰到这样一种情况
我们创建了一个类,却只使用了一次这个类
为了是代码不显得冗杂,我们用lambda表达式来解决

package Lambda;

//Lambda表达式


public class TestLambda {

    //3.静态内部类
    static class Like2 implements TLike{
        @Override
        public void lambda(){
            System.out.println("i like lambda2");
        }
    }

    public static void main(String[] args) {
        TLike like = new Like();
        like.lambda();

        TLike like2=new Like2();
        like2.lambda();

        //4.局部内部类:必须借助接口才能实现,且类没有名字
        class Like3 implements TLike{
            @Override
            public void lambda(){
                System.out.println("i like lambda3");
            }
        }

        TLike like3=new Like3();
        like3.lambda();

        //5.匿名内部类
        like = new TLike() {
            @Override
            public void lambda() {
                System.out.println("i like lambda4");
            }
        };
        like.lambda();

        //6。用Lambda表达式简化
        like = ()->{
            System.out.println("i like lambda5");
        };
        like.lambda();
    }
}

//1.定义一个函数式接口
interface TLike{
    void lambda();
}

//2.实现类
class Like implements TLike{
    @Override
    public void lambda(){
        System.out.println("i like lambda");
    }
}
  • 可以用Lambda表达式的条件是 函数式接口
    化简的形式如下
        //有参数
        love = (a)->{代码块};
        //无参数
        love = ( )->{代码块};

六.线程状态

  • 线程的五大状态如图所示
    请添加图片描述
  • 常用线程方法

请添加图片描述

1.线程停止

  • JDK不推荐使用线程提供的 stop( ) , destory( ) 方法
  • 推荐使用循环次数 + 标志位方法
public class TestStop implements Runnable{

    //1.设置一个标志位
    private boolean flag = true;

    @Override
    public void run(){
        int i = 0;
        while(flag){
            System.out.println("Thread run..."+i++);
        }
    }

    //2.设置一个公开的线程,转换标志位

    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();

        new Thread(testStop).start();
        for(int i=0;i<100;i++)
        {
            System.out.println("main"+i);
            if (i==99)
            {
                testStop.stop();
                System.out.println("线程停止");
            }
        }
    }
}

2.线程休眠

  • sleep 指定当前线程阻塞毫秒数

  • 下面为一个获取系统时间的程序

public class TestSleep{
    public static void tenDown() throws InterruptedException {
        int num = 10;

        while (true){
            //线程休眠
            Thread.sleep(1000);
            System.out.println(num--);
            if (num<=0){
                break;
            }
        }
    }

    public static void main(String[] args) {
        //打印当前系统时间

        //获取系统当前时间
        Date startTime = new Date(System.currentTimeMillis());

        while(true){
            try{
                //线程休眠
                Thread.sleep(1000);
                startTime = new Date(System.currentTimeMillis());//更新当前时间
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));

                //tenDown();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

3.线程礼让

  • 礼让不一定成功
  • 礼让为让当前线程从运行状态转就绪状态
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();

        new Thread(myYield,"虚空").start();
        new Thread(myYield,"银月").start();
    }
}

class MyYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

4.线程强制执行

  • 用join方法实现

  • 主线程与子线程往往同时执行,当使用join方法后,主线程放弃cpu占有权,待当前的子线程执行完后再执行

public class TestJoin implements Runnable{

    @Override
    public void run() {
        for(int i=0;i<10;i++)
        {
            System.out.println("vip 来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        for (int i=0;i<50;i++)
        {
            if(i==20)
            {
                thread.join();
            }
            System.out.println("贫民"+i);
        }
    }
}

5.线程状态的观测

  • Thread.getState( ) 实现
public class TestState {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i=0;i<5;i++)
            {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("¥¥¥¥¥¥¥¥¥");
        });

        //观察启动前的状态
        Thread.State state = thread.getState();
        System.out.println(state);

        //观察启动后的状态
        thread.start();
        state = thread.getState();
        System.out.println(state);

        while(state!=Thread.State.TERMINATED)
        {
            Thread.sleep(200);
            state = thread.getState();
            System.out.println(state);
        }
    }
}

6.线程优先级

  • 给予线程执行的优先度
  • 设置了优先级不一定按照优先级来执行,只是稍微改变了各个线程抢占资源的能力,具体如何还得看cpu的调度
public class TestPriority {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()
                +"-->"+Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();

        Thread t1 = new Thread(myPriority,"t1");
        Thread t2 = new Thread(myPriority,"t2");
        Thread t3 = new Thread(myPriority,"t3");
        Thread t4 = new Thread(myPriority,"t4");


        //先设置优先级再启动
        t1.setPriority(1);
        t2.setPriority(2);
        t3.setPriority(3);
        t4.setPriority(Thread.MAX_PRIORITY);

        //设置优先级只是提高了某一线程抢占资源的机会,不能保证一定先执行
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()
                +"-->"+Thread.currentThread().getPriority());
    }
}

7.守护线程

先说一下线程的分类
线程分为 用户线程守护线程

一般的线程称为用户线程(Daemon = false)
无需考虑程序是否执行完毕,在所有用户线程结束前一直存在 的线程为守护线程(Deamon = true)

常见的守护线程有:记录操作日志,监控内存,垃圾回收等

public class TestDeamon {

    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);
        thread.start();

        new Thread(you).start();

    }
}

class God implements Runnable{
    @Override
    public void run(){
        while(true){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("I always protect you!");
        }
    }
}

class You implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<365;i++)
        {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("happy life");
        }
        System.out.println("=====Bye world!=====");
    }
}

七. 线程同步机制

重点是锁机制,上锁后方法由线程依次调用,主要实现方法为 synchronized 关键字

synchronized(对象){代码块}

这样声明后, 代码块中的对象就被 “上锁了”

{

    private int ticketNums = 10;
    boolean flag = true;

    @Override
    public void run(){
        while(flag){
            buy();
        }
    }

    public static void main(String[] args) {
        TestThread4 ticket = new TestThread4();

        new Thread(ticket,"虚空").start();
        new Thread(ticket,"银月").start();
        new Thread(ticket,"邪后").start();
    }

    //加 synchrinized 后变为同步方法,锁的是实例化后的对象
    public void buy(){
        synchronized (this){
            if(ticketNums>0)
            {
                System.out.println(Thread.currentThread().getName()
                        +"拿到了第"+ticketNums--+"黄贴");
            }
            else
            {
                flag = false;
            }
        }
    }
}

1.扩充:JUC包

JUC包(java.util.concurrent)中有很多并发编程中常用的工具类
链接

import java.util.concurrent.CopyOnWriteArrayList;

public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i=0;i<1000;i++)
        {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

2.死锁

我们通过下面的案例来看看死锁的情况

public class DeadLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"美人鱼");
        Makeup g2 = new Makeup(1,"灰姑娘");

        g1.start();
        g2.start();
    }
}

//口红
class Lipstick{

}

//镜子
class Mirror{

}

class Makeup extends Thread{
    //需要的资源
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;
    String girlName;

    Makeup(int choice,String girlName){
        this.choice=choice;
        this.girlName=girlName;
    }

    @Override
    public void run(){
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    //化妆
    private void makeup() throws InterruptedException {
        if(choice==0)
        {
            synchronized (lipstick){
                System.out.println(this.girlName+"获得口红的锁");
                Thread.sleep(1000);
                synchronized (mirror){
                    System.out.println(this.girlName+"获得镜子的锁");
                }
            }
        }
        else
        {
            synchronized (mirror){
                System.out.println(this.girlName+"获得口红的锁");
                Thread.sleep(2000);
                synchronized (lipstick){
                    System.out.println(this.girlName+"获得镜子的锁");
                }
            }
        }
    }

}

注意以下这两段程序

synchronized (lipstick){
                ...
                synchronized (mirror){
                    ...
                }
synchronized (mirror){
                ...
                synchronized (lipstick){
                    ...
                }
            }

我们知道当代码块执行完毕时锁才会释放,而两个线程却犯了错误,在 手中的锁还未释放时 就申请要对方的锁,导致两把锁(mirror, lipstick)都无法释放,变成了死锁。

按以下方式修改便可解决

 if(choice==0)
        {
            synchronized (lipstick){
                System.out.println(this.girlName+"获得口红的锁");
                Thread.sleep(50);
            }
            synchronized (mirror){
                System.out.println(this.girlName+"获得镜子的锁");
            }
        }
        else
        {
            synchronized (mirror){
                System.out.println(this.girlName+"获得口红的锁");
                Thread.sleep(50);
            }
            synchronized (lipstick){
                System.out.println(this.girlName+"获得镜子的锁");
            }
        }

总结: 死锁的出现原因可总结为以下条件的共同作用

请添加图片描述

3.Lock对象

  • 重点!!!
  • 前面的synchronized称为同步方法,而这里是区别于前者的显示声明锁的方法
  • 在线程编写时推荐使用
class TestLock2 implements Runnable{
    private int ticketnums = 10;
    private final ReentrantLock lock = new ReentrantLock();//锁的声明
    
    @Override
    public void run()
    {
        lock.lock();
        try{//注意搭配try,finally使用
            System.out.println("第"+ticketnums--+"张票");
        }finally {
            lock.unlock();
        }
    }
}

八. 线程通讯协作(生产者消费者问题)

1.管程法

  • 用 notifyAll 和 wait 实现

//管程法
public class TestPC {

    public static void main(String[] args) {
        SynContainer container=new SynContainer();

        new Productor(container).start();
        new Consumer(container).start();
    }

}

//生产者
class Productor extends Thread{
    SynContainer container;

    public Productor(SynContainer container) {
        this.container = container;
    }

    public void run(){
        for(int i=0;i<100;i++)
        {
            System.out.println("生产了第"+i+"只鸡");
            try {
                container.push(new Chicken(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

//消费者
class Consumer extends Thread{
    SynContainer container;

    public Consumer(SynContainer container){
        this.container = container;
    }

    //消费
    @Override
    public void run(){
        for (int i=0;i<100;i++){
            try {
                System.out.println("消费者-->第"+container.pop().id+"只鸡");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

//产品
class Chicken{
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

//缓冲区
class SynContainer{
    //需要一个容器大小
    Chicken[] chickens=new Chicken[10];
    //容器计数器
    int cnt = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) throws InterruptedException {
        //如果容器满了,等待消费者消费
        if(cnt==chickens.length)
        {
            //等待消费者
            this.wait();
        }
        //如果没有满,我们就需要丢入产品
        chickens[cnt]=chicken;
        cnt++;

        //可以通知消费者消费
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop() throws InterruptedException {
        if(cnt==0)
        {
            //等待生产者
            this.wait();
        }
        //如果可以消费
        cnt--;
        Chicken chicken = chickens[cnt];

        //通知生产者生产
        this.notifyAll();

        return chicken;
    }
}

2.信号灯法

  • 设定一个标志位,线程运行时根据 flag 状态来确定是否 wait()

//信号灯法
public class TestPC2 {
    public static void main(String[] args) {
        Movie movie = new Movie();
        new Player(movie).start();
        new Watcher(movie).start();

    }
}

//生产者-->演员
class Player extends Thread{
    Movie movie;
    public Player(Movie movie) {
        this.movie = movie;
    }

    public void run()
    {
        for (int i=0;i<20;i++)
        {
            if(i%2==0)
            {
                this.movie.play("Tears");
            }
            else
            {
                this.movie.play("Matrix");
            }
        }
    }

}

//消费者-->观众
class Watcher extends Thread{
    Movie movie;
    public Watcher(Movie movie) {
        this.movie = movie;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.movie.watch();
        }
    }
}

//产品-->节目
class Movie{
    //演员表演观众等待  T
    //观众观看,演员等待 F

    String voice;//表演的节目
    boolean flag = true;

    //表演
    public synchronized void play(String voice){
        if(!flag)
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("演员表演了:"+voice);
        //通知观众观看
        this.notifyAll();
        this.voice = voice;
        this.flag = !this.flag;
    }

    //观看
    public synchronized void watch()
    {
        if (flag)
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了:"+voice);
        //通知演员表演
        this.notifyAll();
        this.flag=!this.flag;
    }

}

九. 线程池

经常性地创建与销毁线程,会导致消耗大量的资源。我们可以选择提前创建好若干个线程,放入线程池中,使用的时候直接获取,使用完放回池中,可以实现线程的重复利用。

public class TestPool {
    public static void main(String[] args) {
        //1.创建线程池
        //ExecutorService service = Executors.newFixedThreadPool(int nThread); 参数为线程池的大小
        ExecutorService service = Executors.newFixedThreadPool(10);
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //2.关闭连接
        service.shutdown();


    }
}

class MyThread implements  Runnable{

    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java是一种广泛使用的编程语言,有简单、面向对象、跨平台等特点。下面是Java学习的一些重要知识点和学习笔记: 1. Java基础: - 数据类型:Java提供了多种数据类型,包括基本数据类型和引用数据类型。 - 控制流程:学习如何使用条件语句(if-else、switch)、循环语句(for、while)等控制程序的流程。 - 数组:了解如何声明、初始化和操作数组。 - 方法:学习如何定义和调用方法,以及方法的参数和返回值。 - 类和对象:理解类和对象的概念,学习如何定义类、创建对象和使用类的成员变量和方法。 - 继承和多态:掌握继承和多态的概念,了解如何使用继承创建子类,并实现方法的重写和多态的应用。 2. 面向对象编程: - 封装:学习如何使用访问修饰符(public、private、protected)来控制类的成员的访问权限。 - 继承:了解继承的概念和作用,学习如何使用extends关键字创建子类。 - 多态:理解多态的概念和实现方式,学习如何使用父类引用指向子类对象,实现方法的动态绑定。 3. 异常处理: - 异常的分类:了解异常的分类(Checked Exception和Unchecked Exception)和常见的异常类型。 - 异常处理机制:学习如何使用try-catch语句捕获和处理异常,以及使用throws关键字声明方法可能抛出的异常。 4. 输入输出: - 文件操作:学习如何读写文件,包括使用File类、字节流和字符流等。 - 序列化:了解对象的序列化和反序列化,学习如何将对象保存到文件或网络中。 5. 集合框架: - 学习Java提供的集合框架,包括List、Set、Map等常用的集合类,以及它们的特点和用法。 6. 多线程编程: - 学习如何创建和管理线程,了解线程同步和线程间通信的方法。 7. 数据库连接: - 学习如何使用Java连接数据库,执行SQL语句,进行数据的增删改查操作。 以上是Java学习的一些重要知识点和学习笔记,希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值