Java线程

一、程序,进程,线程

程序:为解决某种问题,使用计算机语言编写的一系列指令(代码)的组合.

        本章中的程序,特指的是静态的,安装在硬盘上的代码集合.

进程:运行中的程序(被加载到内存中),是操作系统进行资源分配的最小单位

线程:进程可以进一步细化为线程,是进程内一个徐晓执行单元,

        是CPU进行任务导读的最小单位.

例如:运行中的QQ就是一个进程,操作系统会为这个进程分配内存资源,

一个聊天窗口就认为是一个线程,这多个聊天窗口可以同时被CPU执行,

但是这些聊天窗口属于进程

● 一个进程可以包含多个线程,

● 一个线程只能属于一个进程,线程不能脱离进程而独立运行;

● 每一个进程至少包含一个线程(称为主线程);

● 在主线程中可以创建并启动其它的线程;

● 一个进程内的所有线程共享该进程的内存资源

线程和进程关系

一个进程中可以包含多个线程

一个线程只能隶属于一个线程,线程不能脱离进程而存在

一个进程中至少有一个线程(即主线程) java中的main方法,就是用来启动主线程

在主线程中可以创建并启动其他线程

所有线程都共享进程内存资源.

需求:想在java程序中有几件不相关的事情同时有机会执行

可以在java中创建线程,把一些要执行的任务放在线程中执行,

这样的话,都拥有让CPU执行的权利

二、创建线程

创建线程方式1:

继承Thread类的方式,重写run()

创建自己定义的线程类对象,调用start()启动线程

MyThread extends Thread{

}

创建线程方式2:

类 实现Runable接口,把这个类不饿能称为线程,是一个任务类

重写run()

newThread(任务对象);

调用线程对象的start(),启动线程

以后用第二种更多一些:

1.避免单继承的局限性,因为java是单继承的,继承了Thread类就不能继承其他类了

2.更适合多线程共享同一份资源的场景

package com.ffyc.javathread.demo1;

public class Demo {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println("循环1" + i);
        }
        test();
        System.out.println("最后的代码");
    }

    private static void test() {
        for (int j = 0; j < 10; j++) {
            System.out.println("循环2" + j);
        }
    }
}



package com.ffyc.javathread.demo1;

import com.ffyc.javathread.demo3.MyTask;

public class MyThread extends Thread{

    @Override
    public void run() {
        for(int i=0;i<10000;i++){
            System.out.println("MyThread:"+i);
        }
    }
}



package com.ffyc.javathread.demo1;

public class Test {
    public static void main(String[] args) {
        //创建线程对象
        MyThread myThread=new MyThread();
        //切记不能直接调用run();
        //myThread.run();//就是一个剖痛的方法调用,并不是启动线程
        myThread.start();//启动线程,启动线程后,并不会立即执行,需要操作系统的调度
        for(int i=0;i<10000;i++){
            System.out.println("main:"+i);
        }
    }
}


package com.ffyc.javathread.demo2;
/*
创建一个任务,实现Runnable接口
重写Runnable接口中的run方法
 */
public class MyTask implements Runnable {
    @Override
    public void run() {
        //是那个线程在执行我
        Thread thread=Thread.currentThread();//在任务中,通过方法获得当前正在执行的线程
        System.out.println(thread.getName());
        /*for(int i=0;i<10000;i++){
            System.out.println("mytask:"+i);
        }*/
    }
}



package com.ffyc.javathread.demo2;

public class Test {
    /*
    main方法是用来启动java主线程的
     */
    public static void main(String[] args) {
        MyTask myTask = new MyTask();//创建一个任务对象
        //创建另一个线程
        Thread t1 = new Thread(myTask,"线程1");
        t1.setName("我的线程1");
        t1.start();//启动线程 去执行mytask任务
        Thread t2=new Thread(myTask);
        t2.setName("我的线程2");
        t2.start();
        /*for (int i = 0; i < 10000; i++) {
            System.out.println("main:" + i);
        }*/
    }
}

三、Thread类常用的方法

Thread类表示线程,提供了很多的方法,来对线程进行控制.

run();线程要执行的任务

start();启动java线程的

package com.ffyc.javathread.demo3;

public class MyTask implements  Runnable {
    @Override
    public void run() {
        Thread thread=Thread.currentThread();
        /*if(thread.getName().equals("线程1")){
            try {
                Thread.sleep(2000);//让线程休眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }*/
        System.out.println(thread.getName()+":"+thread.getPriority());
    }
}




package com.ffyc.javathread.demo3;

import com.ffyc.javathread.demo1.MyThread;

public class Test {
    public static void main(String[] args) {
        MyTask myTask=new MyTask();
        Thread t1=new Thread(myTask, "线程1");
        Thread t2=new Thread(myTask,"线程2");
        /*t1.setPriority(8);//设置优先级 1-10之间 默认是5*/
        t2.start();
        try {
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.start();
        System.out.println(Thread.currentThread());
    }
}

构造方法

new Thread(Runable runable);接受一个任务对象

new Thread(Runable runable,String name);接受一个任务对象,并为线程设置名字

setName("线程1");为线程设置名字

String getName();

Thread.currentThread();在任务中获得当前正在执行的线程

setPriority(int p) 1-10之间 默认是5

getPriority(); 获得优先级

sleep(long m);让线程休眠指定的时间 毫秒单位

join();让其他线程等待当前线程结束

四、线程优先级

先来先服务(ECES) 调度算法

短作业优先(SJE) 调度算法

优先级调度算法

高响应比优先调度算法

多级反馈队列调度算法(集合了前几种算法的优点)

五、线程状态

线程生命周期,线程从创建到销毁

期间经历5个状态

新建:new Thread();处于新建状态,此状态还不能被执行

调用start()启动线程 让线程进入到就绪状态

就绪:

获得到CPU执行后,线程进入到CPU执行

运行:运行中的线程可以被切换,回到就绪状态,也可能因为休眠等原因进入阻塞状态

阻塞:线程休眠时问到了 回到就绪状态

死亡:当线程中所有的任务执行完了,线程也就自动销毁

六、守护线程

守护线程也是线程中的一种,区别在于结束,

如果一个线程是守护线程,那么它会等java中其他的线程任务结束后,自动终止.

守护线程是为其他线程服务的,例如jvm中的垃圾回收线程,就是一个守护线程。

package com.ffyc.javathread.demo4;

public class Task implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("我是守护线程");
        }
    }
}



package com.ffyc.javathread.demo4;

public class Test {
    public static void main(String[] args) {
        Task task=new Task();
        Thread thread=new Thread(task);
        thread.setDaemon(true);//设置线程为守护线程, 设置守护线程必须在启动前进行设置
        thread.start();
        for(int i=0;i<10000;i++){
            System.out.println("main:"+i);
        }
    }
}

七、多线程

在一个应用程序中,存在多个线程,不同的线程可以并行的执行任务.

多线程优点:

提高程序的处理能力,效率提高了

提高CPU利用率

大任务的拆分

杀毒软件

垃圾清理

病毒查杀

......

多线程缺点:

线程也是需要占用内存资源和CPU资源(小意思,可以提升硬件设备)

多个线程对同一个共享的资源进行访问,会出现线程安全问题

龟兔赛跑中的flag就是共享资源

八、线程同步

如何解决多个线程访问同一个共享资源时不出问题?

同步=排队+锁 一次只能有一个线程访问共享资源

加锁方式1:使用synchronized关键字修饰代码块和方法

修饰代码块:

同步对象要求:

多个线程用到的必须是同一个对象

可以是java中任何类的对象

作用:用来记录有没有线程机内到同步代码块中

synchronized(同步对象/锁){

//同步代码块,一次只允许一个线程进入

}

修饰方法:

1.锁不需要我们提供了,会默认提供锁对象

2.synchronized如果修饰的是非静态方法,锁对象是this

3.synchronized如果修饰的是静态方法,锁对象是类的Class对象

一个类只有一个Class对象

加锁方式2:使用jdk中提供的ReentrantLock类实现加锁

ReentrantLock只能对某一段代码块加锁,不能对整个方法加锁

synchronized 和 ReentrantLock区别

相同点:都实现了加锁的功能

不同点:synchronized 是一个关键字,ReentrantLock是一个类

synchronized修饰代码块和方法,ReentrantLock只能修饰代码块

synchronized可以隐式的加锁和释放锁,运行过程中如出现了异常可以自动释放

ReentrantLock需要手动的添加锁和释放锁,建议在finally代码中释放锁

package com.ffyc.javathread.demo5;

public class PrintNumThread extends Thread {
    static int num = 0;
    static Object object = new Object();

    /*
    wait();让线程等待,自动释放锁,必须要其他的线程唤醒
    notify();唤醒等待中的线程(调用量wait方法的线程),如果有多个等待,唤醒优先级高的
    notifyAll();唤醒所有等待的线程
                这三个方法都是Object类中定义的方法,
                这三个方法必须在同步代码块中使用,
                这三个方法必须通过锁的对象调用
     */
    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                object.notify();//唤醒等待中的线程
                if (num <= 100) {
                    num++;
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                } else {
                    break;
                }
                try {
                    object.wait();//让线程等待,同时释放了锁,等待的线程不能自己醒来,必须让另一个线程唤醒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}



package com.ffyc.javathread.demo5;

public class Test {
    public static void main(String[] args) {
        PrintNumThread printNumThread = new PrintNumThread();
        Thread t1 = new Thread(printNumThread, "线程1");
        Thread t2 = new Thread(printNumThread, "线程2");
        t1.start();
        t2.start();
    }
}

例:商品的出售问题:

package com.ffyc.javathread.demo6;

/*
柜台角色 共享数据
 */
public class Counter {
    int num = 0;//代表商品

    //负责生产商品的方法 锁对象是this    add和sub用的是同一把锁
    public synchronized void add() {
        if (num == 0) {
            System.out.println("生产者生产了一件商品");
            num = 1;//生产了一件商品
            this.notify();//唤醒消费者线程
        } else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //负责消费商品的方法
    public synchronized void sub() {
        if (num > 0) {
            System.out.println("消费者拿走了一件商品");
            num = 0;//消费者拿走了商品
            this.notify();
        } else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}





package com.ffyc.javathread.demo6;

public class CustomerThread extends Thread {
    Counter counter;

    public CustomerThread(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter.sub();
        }
    }
}





package com.ffyc.javathread.demo6;

/*
生产者线程
 */
public class ProductorThread extends Thread {
    Counter counter;

    public ProductorThread(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while (true) {
            counter.add();
        }
    }
}




package com.ffyc.javathread.demo6;

public class Test {
    public static void main(String[] args) {
        Counter counter = new Counter();//创建的唯一的一个柜台对象
        ProductorThread p = new ProductorThread(counter);
        CustomerThread c = new CustomerThread(counter);
        p.start();
        c.start();
    }
}


售票方式:

package com.ffyc.javathread.ticket1;
/*
卖票
 */

public class TicketThread_back extends Thread {
    static int t = 10;//余票

    @Override
    public void run() {
        while (true) {
            if (t <= 0) {
                break;
            }
            printTicket();
        }
    }

    /*
    出票方法
    synchronized修饰方法,同步对象会有默认值
    synchronized如果修饰的是非静态方法,那么同步对象就是this
    synchronized如果修饰的是静态方法,那么同步对象就是当前类的Class对象
                当前类的Class对象:一个类加载到内存后,会为这个类创建一个位于的Class类对象
     */
    public static synchronized void printTicket() {
        /*try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        if (t > 0) {
            System.out.println(Thread.currentThread().getName() + "卖了" + t);
            t--;
        }
    }

}





package com.ffyc.javathread.ticket1;
/*
卖票
 */

public class TicketThread extends Thread {
    static int t=10;//余票
static Object object=new Object();
    @Override
    public void run() {
        while(true){
            /*
            synchronized修饰代码块
            同步对象:对多个线程对应的对象必须是同一个
                    同步对象可以是Java中任何类的对象
                    用来记录有没有线程进入到同步代码块中的,在对象头中有一块儿空间用来记录有没有现成进入到同步代码块
            synchronzied(同步对象){
            同步代码块
            }
             */
            synchronized (object){
                if(t>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"卖了"+t+"票");
                    t--;
                }else{
                    break;
                }
            }
        }
    }

}




package com.ffyc.javathread.ticket1;

public class Test {
    public static void main(String[] args) {
        TicketThread_back t1=new TicketThread_back();
        t1.setName("窗口1");
        TicketThread_back t2=new TicketThread_back();
        t2.setName("窗口2");
        t1.start();
        t2.start();
    }
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘伊珂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值