Java线程Thread超详细笔记————线程和进程、创建线程、Thread类、优先级、线程状态、守护线程、多线程、线程同步、Lock锁、线程死锁、线程通讯、Callable接口

目录

一、程序,进程,线程

二、线程和进程的关系

三、创建线程

四、Thread类中的方法

五、线程优先级

六、线程状态

七、守护线程

八、多线程的概念

九、线程同步

十、LOCK锁

十一、线程死锁

十二、线程通讯

十三、新增创建线程方式——Callable接口


一、程序,进程,线程

1.程序是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码。

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

3.线程可进一步细化为线程,是一个进程内部的最小执行单元,是操作系统进行任务调度的最小单元,隶属于进程。

二、线程和进程的关系

一个线程只属于一个进程,线程不能脱离进程。

一个进程至少一个线程(主线程),比如java中的main方法就是启动主线程的。

三、创建线程

创建线程有两种方式:继承Thread类的方式和实现Runnable接口的方式

1、继承Thread类的方式

扩展Thread类,重写其中的run方法

Thread类是java提供的对线程进行管理的类

①.定义:

public class MyThread extends Thread {

public void run() {

在线程中要执行的任务都写在run方法中

}

}

②.调用:

MyThread thread = new MyThread();

//myThread.run(); 调用run(),不能启动线程,就是普通的方法调用

thread.start();//启动线程

2、实现Runnable接口的方式

(线程任务类 implements Runnable)

实现Runnable接口是以后创建线程常用的方式:

避免直接继承Thread类,导致我们自己的类,不能再继承其他类。

①.定义:

public class MyThread implements Runnable{

@Override

public void run() {

//要执行的任务写在run方法中

}

}

}

②.调用:

线程执行任务

MyThread r = new MyThread();

创建一个线程作为外壳,将r包起来,

Thread thread = new Thread(r);

thread.start();

3、继承方式和实现方式的联系与区别

【区别】:

继承Thread: 线程代码存放Thread子类run方法中。

实现Runnable:线程代码存在接口的子类的run方法。

【实现Runnable的好处】

1)避免了继承Thread后不能继承其他类的局限性。

2)多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

四、Thread类中的方法

run();线程任务执行方法

start();启动线程

Thread(target);创建线程,并指定任务

currenThread();返回对当前正在执行的线程对象的引用

getName(); 获得线程名称

setName();设置线程名称

setPriority(int newPriority);设置线程的优先级(默认5,最大10,最小1)

getPriority();返回线程的优先级

join();等待线程终止

yield();线程让步

sleep(long millis);让线程休眠(毫秒)

[注意]  重写的方法不能抛出异常,只能使用try{}catch(){}

五、线程优先级

计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能执行任务。

优先级较高的线程有更多获得CPU的机会,反之亦然

Thread类有如下3个静态常量来表示优先级

● MAX_PRIORITY:取值为10,表示最高优先级。

● MIN_PRIORITY:取值为1,表示最底优先级。

● NORM_PRIORITY:取值为5,表示默认的优先级。

六、线程状态

新建:新创建了一个线程对象。

象处于新建状态

就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时

间片,此时它已具备了运行的条件,只是没分配到CPU资源

运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run

()方法定义了线程的操作和功能

阻塞:阻塞状态是指线程因为某种原因放弃了cpu 使用权,并临时中止自己的执行进入阻塞状态,等待线程进入可运行状态

死亡:线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

七、守护线程

1、Java中的线程分为两类:用户线程和守护线程。

2、只有当JVM实例中最后一个非守护线程结束时,守护线程才会随着JVM一同结束工作。

3、守护线程的作用是为其他线程的运行提供便利服务,守护线程最典型的应用是GC(垃圾回收器)。

4、设置守护线程: setDaemon(boolean on)

[注意]  设置线程为守护线程必须在启动线程之前,不然会有异常。

八、多线程的概念

1、多线程:一个应用程序内部,可以同时执行多个任务。

2、优缺点:

①.多线程的优点:

提高程序的处理能力,响应速度。

提高cpu利用率,压榨硬件的价值

②.提升程序结构

多线程的缺点:

对内存和cpu的要求提高了,提升硬件性能改善。

多个线程对同一个共享资源进行访问会相互影响。

九、线程同步

1、并发与并行:

并发:在同一个时间节点上,多个事情同时进行

并发:在一个时间段内,多个事情依次执行

解决办法:

排队+锁

①.几个线程之间要排队

②.在访问时加入锁机制

2、synchronized同步锁

①.修饰一段代码块(同步代码块)

synchronized (同步锁){ 
// 需要被同步的代码; 
} 

②.在方法声明中,表示整个方法,为同步方法:

synchronized(同步对象/锁对象){

}

同步对象/锁对象:

用来记录有没有线程进入到同步代码块, 要求是唯一的对象,多个线程对应的是同一个对象(充当锁标记)。

public synchronized void show (String name){ 
// 需要被同步的代码; 
}

synchronized修饰非静态的方法时,锁对象是this synchronized修饰静态方法时,锁对象是当前类的Class对象。

eg:模拟卖票

public class TicketThread implements Runnable {
    static int num = 10;
    static Object obj = new Object();
​
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (num > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    num--;
                } else {
                    break;
                }
            }
​
        }
    }
}
​
public class Test {
    public static void main(String[] args) {
        TicketThread ticketThread = new TicketThread();
        
        Thread thread1 =     new Thread(ticketThread, "窗口1");
        thread1.start();
​
        Thread thread2 = new Thread(ticketThread, "窗口2");
        thread2.start();
    }
}

3、synchronized关键字:

(1).修饰一个普通方法:被修饰的方法称为同步方法,其作用域是整个方法,作用的对象是调用这个方法的对象。

(2).修饰静态方法:作用域是整个静态方法,作用的对象是这个类,与类的对象无关。

(3).修饰代码块:被修饰的代码块称为同步代码块,其作用域是大括号{}括起来的代码块,作用的对象是括号中的对象(类和对象)。

十、LOCK锁

1、ReentrantLock类实现了Lock,它与synchronized关键字作用差别不大,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。

2、ReentrantLock 和 synchronized 区别:

①.synchronized是关键字,依靠底层编译后的指令来实现。 synchronized可以修饰代码块和方法 synchronized是隐式锁,自动添加锁,同步代码块执行完毕或者出现异常,锁会自动释放

②.ReentrantLock是java.util.concurrent.locks包下的一个类,是依靠java代码实现控制。

ReentrantLock只能修饰代码块 ReentrantLock是手动添加,手动释放。

public class TicketThread implements Runnable{
    int num = 10;
    Lock lock = new ReentrantLock();//ReentrantLock只能对代码块实现 不能修饰方法
    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();//获取锁
                if (num > 0) {
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    num--;
                } else {
                    break;
                }
            }finally {
                lock.unlock();//释放锁
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        PrintTicket printTicket = new PrintTicket();
        Thread t1 = new Thread(printTicket,"窗口1");
        t1.start();
        Thread t2 = new Thread(printTicket,"窗口2");
        t2.start();
    }
}

十一、线程死锁

1、多个线程分别占用对方需要的资源,等待对方释放资源(一般至少一个锁的嵌套)。

2、设计程序时需要考虑清楚锁的顺序,尽量减少锁的嵌套使用。

十二、线程通讯

1、线程通讯指的是线程间的相互作用。

2、方法:

.wait(); 线程进入阻塞状态,释放同步监听器,必须使用锁对象来调用wait。

.notify(); 唤醒被wait的线程(或优先级最高的线程)

.notifyAll(); 唤醒所有wait的线程

[注意]  .wait(); .notify(); .notifyAll();三个方法必须使用在同步代码块或同步方法中。

3、wait();和sleep();的区别:

①.wait是Object中的方法,而sleep是线程中的方法

②.wait的锁会释放并加入等待队列,而sleep的锁不会释放

③.wait依赖于关键字sychronized,而sleep不依赖于同步器synchronized

④.sleep不需要被唤醒,但wait需要被唤醒

4、典型案例:

生产者/消费者问题

public class Counter{
    int num=1;
    public synchronized void add(){
        if(num==0){
            this.num=1;
            System.out.println("生产者生产了一件商品");
            this.notify();
        }
        else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
public synchronized void sub(){
        if(num>0){
            this.num--;
            System.out.println("消费者拿走了一件商品");
            this.notify();
        }else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Customer extends Thread{
    Counter counter=new Counter();
    public Customer(Counter counter){
        this.counter=counter;
    }
    @Override
    public void run() {
        while (true){
            counter.add();
        }
    }
}
public class Producer extends Thread {
    Counter counter = new Counter();
    public Producer(Counter counter) {
        this.counter = counter;
    }
    @Override
    public void run() {
        while (true) {
            counter.sub();
        }
    }
}
​
public class Test {
    public static void main(String[] args) {
        Counter counter = new Counter();
        new Producer(counter).start();
        new Customer(counter).start();
    }
}

十三、新增创建线程方式——Callable接口

1、实现Callable接口与使用Runnable相比,Callable功能更强大些.

• 相比run()方法,可以有返回值

• 方法可以抛出异常

• 支持泛型的返回值

但需要借助FutureTask类,获取返回结果

2、接收任务

FutureTask<Integer> futureTask = new FutureTask(任务);

3、创建线程

Thread t = new Thread(futureTask);

t.start();

Integer val = futureTask.get();获得线程call方法的返回值

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值