Java线程

一.程序,进程,线程

1.程序

程序是为完成某种功能,使用计算机语言编写的一系列指令的集合。指的是静态的代码(安装在电脑上的文件)。

2.进程

进程也可以称为程序,但是是运行中的程序。进程是操作系统进行资源分配的最小单位。

3.线程

进程可以进一步细化为线程,线程就是进程中一个最小的执行单元,是cpu进行调度的最小单元。

eg:qq(进程)中的一个聊天窗口。

一个单核的cpu如何运行多个任务?

通过切换进程,但是这样开销会很大。

进程和线程之间的关系:

1.一个进程中可以包含多个线程。(一个运行中的qq可以开多个聊天窗口)

2.一个线程只能隶属于一个进程(qq聊天窗口只能属于qq的),并且线程不能脱离进程独立运行。

3.一个进程中至少包含一个线程(也就是主线程,java中的main方法就是用来启动主线程的)

4.在主线程中可以创建并启动其他的线程。

5.一个进程内所有线程共享该进程的内存资源。

二.创建线程

Thread类

创建一个类必须继承Thread类,并且去重写里面的run()方法,方法体可以是任何执行代码。

注意:Thread 类中的run()方法和start()方法的区别:run()方法只是需要重写的方法,只是一个简单的类中方法,并不是开启线程的方法,start()方法才是真正开启线程的方法,可以实现线程的单独实现。

创建线程方法1:

写一个类去继承 Thread类**重写run()**方法,线程中要执行的任务都写在run()方法中。

线程通过start()开启后,会由操作系统去调用run()方法(本地方法)。

java 复制代码
package com.zyk.Thread.demo1;

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

   }
}


package com.zyk.Thread.demo1;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        //调用run()方法和在一个main中没有任何区别,只是调用方法而已,不是开启线程
        //myThread.run();
        // start()才是真正的开启线程
        myThread.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main:"+i);
        }
    }
}

运行结果:

创建线程方法2:

通过自定义一个类去实现Runnable接口,重写接口中的run()方法,去定义执行任务。

然后通过new出来的Thread类对象,通过构造方法去创建并开启线程。

java 复制代码
package com.zyk.Thread.demo2;

public class Task implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("自定义线程:"+i);
        }
    }
}



public class ThreadTest {
    public static void main(String[] args) {
        //定义了执行任务
        Task task=new Task();
        //创建线程
        Thread thread=new Thread(task);
        thread.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main:"+i);
        }
    }
}

运行结果:

通过实现Runnable接口去创建线程的方法的优点:

1.因为java是单继承,一旦继承一个类就不能再继承其他类,避免了单继承的局限。

2.适合多线程来处理同一份资源时使用。

创建线程方法3:

通过实现Callable接口。

java 复制代码
package com.zyk.Thread.demo9;

import java.util.concurrent.Callable;

public class SumTask implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        Integer sum=0;
        for (int i = 0; i < 100; i++) {
            sum+=i;
        }
        return sum;

    }
}



package com.zyk.Thread.demo9;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        SumTask sumTask=new SumTask();
        FutureTask<Integer> futureTask=new FutureTask<>(sumTask);
        Thread thread=new Thread(futureTask);
        thread.start();
        Integer i = futureTask.get();
        System.out.println(i);
    }
}

使用此方法在开启线程时需要借助FutureTask作为中转new线程。

优点:

此方法的call()方法可以有返回值,并且可以去抛出异常。(与第二种创建线程方法的区别

三.Thread常用方法

1.run()方法

用来定义线程中需要执行的任务代码。

2.Thread.currentThread()

获取到当前线程。( 在哪个线程中执行就拿的是哪个线程)

3.myThread.setName(" ");

对线程重新命名。

4.setPriority();

设置线程优先级。(1<=优先级<=10,作用是为操作系统中的调度算法提供的)

5.getId();

获取线程id。

6.getName();

获取线程名字。

7.getPriority();

获取线程优先级。(java中默认为5)

8.getState();

获取线程状态。

9.start();

启动线程。

10.sleep(long millis);

让线程阻塞休眠milis毫秒。

11.join();

等待调用join()方法的线程执行完毕,其他线程再执行。(让其他线程等待当前线程执行完之后再执行)

12.yield();

使当前线程主动让步重新进入就绪队列排队。

四.线程状态

新建态:刚刚创建了一个线程对象,并没有启动。

就绪态(可运行):调用start()之后,线程就进入了就绪态,进入了操作系统的调度队列中。

运行态:获得了cpu的执行权,就进入到了cpu执行。

阻塞态:例如调用了sleep(),有线程调用了join(),线程中调用了Scanner .....

死亡/销毁态:run()方法中任务执行完毕。

状态切换

新建态-->就绪态:调用start()方法。

运行态-->就绪态:1.cpu正常切换线程 2.线程调用yield()

运行态-->销毁态:1.run()正常执行完毕 2.调用了Thread中的stop()方法 3.线程调用中出现异常并没有处理、

运行态-->阻塞态:1.调用sleep() 2.调用join(),注意是除了调用join()线程之外的其他线程进入阻塞态 3.Scanner控制台输入 4.等待同步锁 5.wait()

阻塞态-->就绪态:1.sleep()时间到了 2.调用join()的线程执行完毕 3.控制台输入完毕 4.获取到同步锁 5.其他线程唤醒notify()

五.多线程

在一个程序中创建多个线程执行。

优点:

1.提高了程序的响应速度

2.提高了cpu的利用率

3.改善程序结构,例如将一个大的任务拆分成多个小任务

缺点:

1.线程多了,占用内存

2.cpu开销变大

3.线程之间同时对共享资源的访问会相互影响,如果不加以控制会导致数据错误(eg:买票 抢购 秒杀......)

如何解决此类问题呢 :

排队+锁

几个线程之间要排队,一个个对共享资源进行操作,而不是同时操作。

为了保证数据在方法中被访问的正确性,在访问时加入锁机制。

加锁方式

synchronized关键字

synchronized修饰方法时,同步锁对象不需要我们去提供

1.非静态的方法--锁对象默认为this

2.静态方法--锁对象默认为当前类的class对象(类的对象,一个类的对象只有一个)

java 复制代码
synchronized(同步锁对象){
    //同步代码块
}
//同步锁对象作用:用来记录有没有线程进入到同步代码块之中,如果有线程进入到同步代码块,那么其他线程就不能进入到同步代码块之中,直到上一个线程执行完同步代码块的内容,释放锁之后,其他线程才能进入。
//同步锁对象要求:同步锁对象必须是要唯一的,可以是任意类的对象,多个线程对应一个同步锁对象,因此必须是要用static修饰的.
java 复制代码
public synchronized void print(){

}


eg:
public class TicketThread extends Thread{
    //票数
    static int num=10;
    static Object obj=new Object();

    @Override
    public void run() {
        this.print();
    }

    public static synchronized void print(){
        while(true){
            if(num<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+ "抢到了第" + num + "张票");
            num--;

        }
    }
}

使用类继承Thred类:

java 复制代码
package com.zyk.Thread.demo4;

public class TicketTest {
    public static void main(String[] args) {
        TicketThread t1=new TicketThread();
        t1.setName("窗口1");
        t1.start();

        TicketThread t2=new TicketThread();
        t2.setName("窗口2");
        t2.start();
    }
}
package com.zyk.Thread.demo4;

public class TicketThread extends Thread{
    //票数
    static int num=10;
    static Object obj=new Object();

    @Override
    public void run() {
        this.print();
    }

    public static synchronized void print(){
        while(true){
            if(num<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+ "抢到了第" + num + "张票");
            num--;
        }
    }

/*    @Override
    public void run() {
        while (true){
            //参数为同步锁对象
            synchronized (obj){
                if (num > 0) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + "抢到了第" + num + "张票");
                    num--;
                } else {
                    break;
                }
            }
        }
    }*/
}

实用类实现Runnable接口:

java 复制代码
package com.zyk.Thread.demo5;

public class TicketTest {
    public static void main(String[] args) {
        TicketTask ticketTask=new TicketTask();

        Thread t1=new Thread(ticketTask);
        t1.setName("窗口1");
        t1.start();
        Thread t2=new Thread(ticketTask);
        t2.setName("窗口2");
        t2.start();
    }
}


package com.zyk.Thread.demo5;

public class TicketTask implements Runnable {
    int num = 10;

    @Override
    public void run() {
        synchronized (this) {
            while (true) {
                if (num > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + "抢到了第" + num + "张票");
                    num--;
                } else {
                    break;
                }
            }
        }
    }
}

ReentrantLock类

ReentrantLock是一个java的类

他只能修饰代码块,并且加锁释放锁都是由java代码块实现的。

一般加锁和释放锁需要在 try--finally发代码块中执行,因为如果加锁之后的代码中出现了异常,这个时候锁就必须要关闭。

java 复制代码
package com.zyk.Thread.demo6;

public class TicketTest {
    public static void main(String[] args) {
        TicketThread t1=new TicketThread();
        t1.setName("窗口1");
        t1.start();

        TicketThread t2=new TicketThread();
        t2.setName("窗口2");
        t2.start();
    }
}



package com.zyk.Thread.demo6;

import java.util.concurrent.locks.ReentrantLock;

public class TicketThread extends Thread {
    //票数
    static int num = 10;
    static ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        try {
            reentrantLock.lock();
            while (true) {
                if (num > 0) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + "抢到了第" + num + "张票");
                    num--;
                } else {
                    break;
                }
            }
        } finally {
            reentrantLock.unlock();
        }
    }
}

两种加锁方式的区别

1.synchronized 是一个关键字;ReentrantLock是一个类。

2.synchronized既可以修饰代码块,有可以修饰方法。

ReentrantLock只能修饰代码块。

3.synchronized的加锁和释放锁由底层编译后的指令去实现,自动执行,出现异常会自动释放锁。(synchronized属于非公平锁)

ReentrantLock的加锁和释放锁由java代码实现(底层是同步队列,属于公平锁),需要手动操作,并且一般使用try--finally,一旦出现异常保证能释放锁。

线程通信

线程通讯指的是多个线程通过相互牵制,相互调度,既线程间的相互作用。

涉及三个方法:

1.wait(),执行此方法,当前线程进入阻塞状态,并释放同步锁对象。

2.notify(),唤醒线程

3.notifyAll()

wait()、notify()、notifyAll()方法被调用在synchronized 代码块中,且必须使用锁对象调用,才能实现线程之间的相互牵制相互调度。

java 复制代码
package com.zyk.Thread.demo7;

public class PrintNumTest {
    public static void main(String[] args) {
        PrintNumThread p1=new PrintNumThread();
        p1.start();

        PrintNumThread p2=new PrintNumThread();
        p2.start();

    }
}



package com.zyk.Thread.demo7;

public class PrintNumThread extends Thread{
    static int num=1;
    static String obj=new String();
    @Override
    public void run() {
        synchronized (obj){
            while (num<=100){
                obj.notify();
                System.out.println(Thread.currentThread().getName()+":"+num);
                num++;
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

生产消费者问题

java 复制代码
package com.zyk.Thread.demo8;
//消费者线程
public class CustomerThread extends Thread{

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

    @Override
    public void run() {
        try {
            counter.sub();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}


package com.zyk.Thread.demo8;
//生产者线程
public class ProductThread extends Thread{
    Counter counter;
    public ProductThread(Counter counter) {
        this.counter=counter;
    }

    @Override
    public void run() {
        try {
            counter.add();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}



package com.zyk.Thread.demo8;

//柜台类
public class Counter {
    int num=1;

    /**
     * 生产者调用
     * 每次进来的时候都只能是一个线程
     */
    public synchronized void add() throws InterruptedException {
        while (true){
            if(num==1){
                this.wait();
            }else{
                //先唤醒消费者线程
                this.notify();
                num=1;
                System.out.println("生产者生产了一件产品");
                Thread.sleep(1000);
            }
        }
    }

    /**
     * 消费者调用
     */
    public synchronized void sub() throws InterruptedException {
        while (true){
            if(num==0){
                this.wait();
            }else {
                this.notify();
                num=0;
                System.out.println("消费者消费了一件产品");
                Thread.sleep(1000);
            }
        }
    }
}



package com.zyk.Thread.demo8;
//测试类
public class test {
    public static void main(String[] args) {
        Counter counter=new Counter();
        ProductThread productThread=new ProductThread(counter);
        productThread.start();

        CustomerThread customerThread=new CustomerThread(counter);
        customerThread.start();
    }
}

wait()和sleep()方法的区别

wait():

1.是Object类中的方法,用于同步锁对象调用。

2.需要其他的线程进来唤醒(notifiy() notifiyAll())。

3.wait()会自动释放锁。

sleep():

1.是Thread类中的方法。

2.线程在等待休眠时间过之后会自动唤醒。

3.sleep()不会自动释放锁。

相关推荐
MSTcheng.2 分钟前
【C++】C++11新特性(二)
java·开发语言·c++·c++11
晓13135 分钟前
第七章 【C语言篇:文件】 文件全面解析
linux·c语言·开发语言
愚者游世5 分钟前
Delegating Constructor(委托构造函数)各版本异同
开发语言·c++·程序人生·面试·改行学it
一 乐6 分钟前
校园二手交易|基于springboot + vue校园二手交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端
KIKIiiiiiiii7 分钟前
微信个人号API二次开发中的解决经验
java·人工智能·python·微信
梵刹古音7 分钟前
【C语言】 指针基础与定义
c语言·开发语言·算法
80530单词突击赢8 分钟前
SpringBoot整合SpringMVC全解析
java·spring boot·后端
Ekehlaft11 分钟前
这款国产 AI,让 Python 小白也能玩转编程
开发语言·人工智能·python·ai·aipy
rit843249913 分钟前
MATLAB中Teager能量算子提取与解调信号的实现
开发语言·matlab
开源技术15 分钟前
Python GeoPandas基础知识:地图、投影和空间连接
开发语言·ide·python