java线程知识点

本文深入探讨了Java线程的实现,包括实现Runnable接口的方式以及线程优先级的概念。同时,介绍了多线程的使用,并通过代码示例展示了如何在Java中实现线程同步,如使用`synchronized`关键字进行上锁。此外,还提及了线程通信的重要性和新增的创建线程方式。
摘要由CSDN通过智能技术生成
程序,进程,线程
线程和进程的关系
创建线程
Thread类中方法
线程优先级
线程状态
守护线程
多线程的概念
线程同步
Lock
线程通信
新增创建线程方式
一. 程序 , 进程 , 线程
程序:是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码.
进程:就是正在执行的程序,从Windows角度讲,进程是操作系统进行资源分配 的最小单位.
线程:进程可进一步细化为线程,是一个进程内部的最小执行单元,是操作系统进行任务调度的最小单元,隶属于进程

二.线程和进程的关系
一个进程可以包含多个线程,一个线程只能属于一个进程,线程不能脱离进程
而独立运行;
每一个进程至少包含一个线程(称为主线程);在主线程中开始执行程序,
java程序的入口main()方法就是在主线程中被执行的。
在主线程中可以创建并启动其它的线程;
一个进程内的所有线程共享该进程的内存资源
三.创建线程
继承Thread类的方式
实现Runnable接口的方式
继承Thread类的方式:
代码实现方式如下:
public class Mythread extends Thread{//thread类是java提供的对线程进行管理的类
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            System.out.println("thread:"+i);
        }
    }
}
public class text {
    public static void main(String[] args) {
        System.out.println("main开始");
        Mythread mythread = new Mythread();//创建了一个线程
                  mythread.start();//这里不能调用自己重写的run方法,如果调用了run相当于只有一个主线程,没有执行自己创建的线程
        for (int i = 0; i <1000 ; i++) {
            System.out.println("main结束");
        }

    }
}

实现Runnable接口的方式:

java.lang.Runnable接口中仅仅只有一个抽象方法:
   public void run()
可以通过实现Runnable接口的方式来实现线程,只需要实现其中的run方法即可
Runnable接口的存在主要是为了解决Java中不允许多继承的问题
定义:
public class MyThread implements Runnable{
@Override
public void run() {
……
}
}
线程执行任务
MyThread r = new MyThread();
创建一个线程作为外壳,将r包起来,
Thread thread = new Thread(r);
thread.start();
继承方式和实现方式的联系与区别
继承Thread: 线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。
实现Runnable的好处:
1)避免了单继承的局限性
2)多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处
理同一份资源。

四.Thread 类中方法
void start() 启动线程
final String getName() 返回线程的名称
final void setPriority(int newPriority) 设置线程的优先级
final int getPriority() 返回线程的优先级
final void join() 等待线程终止
static Thread currentThread() 返回对当前正在执行的线程对象的引用
static void sleep(long millis) 让当前正在执行的线程休眠(暂停执行), 休眠时间由milli s(毫秒)指定
yield() 线程让步
代码实现:
public class text1 {
    public static void main(String[] args) throws InterruptedException {
        Threaddemo1 threaddemo1 = new Threaddemo1();
        Thread thread = new Thread(threaddemo1);
        thread.start();

        Thread thread1 = new Thread(threaddemo1);
          thread.join();//线程结束
               thread1.start();
        thread1.setName("线程1");//获取线程的名字
        thread.setName("线程0");

        System.out.println(thread.getPriority());//获取线程的优先级,默认优先级是5
        /*thread.setPriority(10);//优先级最大为10,最小为0
        thread1.setPriority(0);//设置优先级后,不一定高的每次先执行,需按操作系统调用*/
    }
}
public class Threaddemo1 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            /*try {
                Thread.sleep(1000);//休眠1000毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
           /* if(i%10==0){
                Thread.yield();//线程让步,主动让出CPU执行权,可直接进入就绪队列中
            }*/
            System.out.println(Thread.currentThread().getName()+":"+i);//currentThread获取当前所在的线程
        }
    }
}

五.线程优先级

计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能执行任务;
优先级较高的线程有更多获得CPU的机会,反之亦然;
优先级用整数表示,取值范围是1~10,一般情况下,线程的默认优先级
都是5,但是也可以通过setPriority和getPriority方法来设置或返回优先级;
注意: 优先级最高为10,最低为0
●调度策略
● 时间片
● 抢占式:高优先级的线程抢占CPU
●Java的调度方法
● 同优先级线程组成先进先出队列,使用时间片策略
● 对高优先级,使用优先调度的抢占式策略
Thread类有如下3个静态常量来表示优先级
MAX_PRIORITY:取值为10,表示最高优先级。
MIN_PRIORITY:取值为1,表示最底优先级。
NORM_PRIORITY:取值为5,表示默认的优先级
六.线程状态
线程在它的生命周期中会处于不同的状态:
1新建   2就绪   3运行   4阻塞   5销毁
线程的状态:
新建 :当一个Thread类或其子类的对象被声明并创建时,新生的线程对
象处于新建状态
就绪: 处于新建状态的线程被start()后,将进入线程队列等待CPU时
间片,此时它已具备了运行的条件,只是没分配到CPU资源
运行: 当就绪的线程被调度并获得CPU资源时,便进入运行状态,run
()方法定义了线程的操作和功能
阻塞: 在某种特殊情况下,被人为挂起或执行输入输出操作时,让出
CPU并临时中止自己的执行,进入阻塞状态
死亡: 线程完成了它的全部工作或线程被提前强制性地中止或出现异常
导致结束
以单核为例:

 

七.守护线程
Java 中的线程分为两类:用户线程和守护线程
任何一个守护线程都是整个 JVM 中所有非守护线程的保姆 , 只要当前 JVM 实例中尚存在
任何一个非守护线程没有结束,守护线程就全部工作;
只有当最后一个非守护线程结束时,守护线程随着 JVM 一同结束工作。
守护线程的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC
( 垃圾回收器 ) ,它就是一个很称职的守护者。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果
用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没
有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了 .
设置守护线程 : setDaemon(boolean on)
注意 : 设置线程为守护线程必须在启动线程之前,否则会跑出一个IllegalThreadStateException异常
代码实现:
public class text {
    public static void main(String[] args) {
        new MyThread().start();

        DemonThread demonThread = new DemonThread();
        demonThread.setDaemon(true);
        demonThread.start();//设置守护线程后,当其他用户线程结束后,守护线程才随着JVM结束工作
    }
}
//用户线程
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
//守护线程
public class DemonThread extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println("我是守护线程,一直在你身边");
        }
    }
}

八.多线程的概念

多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
何时需要多线程
程序需要同时执行两个或多个任务。
程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、
网络操作、搜索等。
多线程的优点
提高程序的响应.
提高CPU的利用率.
改善程序结构,将复杂任务分为多个线程,独立运行.
多线程的缺点
● 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
● 多线程需要协调和管理,所以需要CPU时间跟踪线程;
● 线程之间对共享资源的访问会相互影响;

代码实现:

public class text {
    public static void main(String[] args) {
        //创建多线程
        System.out.println("main开始");
        threaddemo threaddemo = new threaddemo();
        Thread thread = new Thread(threaddemo,"线程0");
               thread.start();

        threaddemo threaddemo1 = new threaddemo();
        Thread thread1 = new Thread(threaddemo1,"线程1");//这里也可以为其指定自己的线程名称
              thread1.start();
        System.out.println("main结束");
    }
}
public class threaddemo implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            //用来区分多个线程,系统会为这几个线程自己定义名字
        }
    }
}
九.线程同步
并发与并行
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事,一个时间点
并发:在一个时间段内一次执行操作.例如卖票,抢购,秒杀看似同时进行,
实际是一个一个执行,一个时间段
多线程同步
多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线程“同步”机制,
即各线程间要有先来后到;
同步就是排队+锁:
几个线程之间要排队,一个个对共享资源进行操作,而不是同时进行操作;
为了保证数据在方法中被访问时的正确性,在访问时加入锁机制
以买票为例:
两个窗口分别售票,票数为10张
分别使用继承Thread和实现Runnable两种方式实现
(1)继承Thread
需要注意:继承Thread方法会创建俩个对象,所以方法坏人属性都需要加上static修饰,表明只有一份
public class text {
    public static void main(String[] args) {
        ticketDemo ticketDemo = new ticketDemo();
        ticketDemo.start();
        ticketDemo.setName("窗口1");

        ticketDemo ticketDemo1 = new ticketDemo();
        ticketDemo1.start();
        ticketDemo1.setName("窗口2");
    }
}
public class ticketDemo extends Thread {
    static int num = 10;//static修饰,只有一份
    static String s = new String();
    @Override
    public void run() {
        synchronized (s){
            while (true){
                if(num>0){
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    num--;
                }else {
                    break;
                }
            }
        }

    }
}

(2)实现Runnable接口

这种是调用方法,将方法用synchronized修饰,上锁

public class runndemo1 extends Thread{
    static int num = 10;
    @Override
    public void run() {
        while (true){
            if(num<=0){
                break;
            }else {
                print();
            }
        }
    }
    public static synchronized void print(){
        if(num>0){
            System.out.println(Thread.currentThread().getName()+":"+num);
            num--;
        }
    }
}
public class text {
    public static void main(String[] args) {
        runndemo1 runndemo1 = new runndemo1();
        runndemo1.start();
        runndemo1.setName("窗口1");

        runndemo1 runndemo2 = new runndemo1();
        runndemo2.start();
        runndemo2.setName("窗口2");

    }
}
public class text1 {
    public static void main(String[] args) {
        rundemo2 rundemo2 = new rundemo2();
        Thread thread = new Thread(rundemo2,"窗口1");

        Thread thread1 = new Thread(rundemo2,"窗口2");
        
        thread.start();
        thread1.start();
    }
}
public class rundemo2 implements Runnable {
    int num = 10;

    @Override
    public void run() {
        synchronized (this) {
            while (true) {
                if (num > 0) {
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    num--;
                } else {
                    break;
                }
            }

        }
    }
}

上面这种是直接在运行的程序里面加锁

确保一个时间点只有一个线程访问共享资源。可以给共享资源加一把锁,哪个线程获取了这把锁,才有权利访问该共享资源。
在Java代码中实现同步:
使用synchronized(同步锁)关键字同步方法或代码块。
synchronized (同步锁){
// 需要被同步的代码;
}
synchronized还可以放在方法声明中,表示整个方法,为同步方法。
例如:
public synchronized void show (String name){
// 需要被同步的代码;
}
线程同步
同步锁synchronized(对象)
同步锁可以是任何对象,必须唯一,保证多个线程获得是同一个对象(用
来充当锁标记).
同步执行过程
1.第一个线程访问,锁定同步对象,执行其中代码.
2.第二个线程访问,发现同步对象被锁定,无法访问.
3.第一个线程访问完毕,解锁同步对象.
4.第二个线程访问,发现同步对象没有锁,然后锁定并访问
一个线程持有锁会导致其他所有需要此锁的线程挂起;在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
十.Lock (锁
Lock
从JDK 5.0开始,Java提供了更强大的线程同步机制-通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先Lock对象。
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁
注意:synchronized锁需要出现异常或者线程执行完毕会释放,Lock锁必须主动释放
十一.线程死锁
死锁
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步 资源,就形成了线程的死锁.
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续.
案例:中国人和美国人吃饭
正常情况:
中国人:两个筷子
美国人:刀和叉
设计时考虑清楚锁的顺序,尽量减少嵌套的加锁交互数量。
特殊情况:
中国人: 一个筷子 一把刀
美国人:一个筷子 一把叉
代码实现:
假如true线程先进来,会进入object代码块里,然后线程false进入了object1代码块里执行程序,当线程以走到了object1时,被线程二占用了,动不了了,当然线程二也卡在了线程正在执行的程序里
public class diedemo extends Thread {
    Object object = new Object();
    Object object1 = new Object();
    boolean flag;

    public diedemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if(flag){
            synchronized (object){
                System.out.println("你好");
                synchronized (object1){
                    System.out.println("你不好");
                }
            }

        }
       else {
            synchronized (object1){
                System.out.println("我好");
                synchronized (object){
                    System.out.println("我不好");
                }
            }

        }
    }
}

public class textdie {
    public static void main(String[] args) {
        new diedemo(true).start();
        new diedemo(false).start();
    }
}

十二.线程通信

线程通讯指的是多个线程通过相互牵制,相互调度,即线程间的相互作用。
涉及三个方法:
.wait一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
.notify一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
.notifyAll一旦执行此方法,就会唤醒所有被wait的线程。
注意:
.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方
法中。
例子:
两个线程交替打印1-100之间的数字
public class suodemo implements Runnable{
    int i = 0;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                this.notify();//唤醒所有等待的线程,在同步代码块中执行
                System.out.println(Thread.currentThread().getName()+":"+i++);
                if(i>100){
                    break;
                }
                try {
                    this.wait();//wait必须使用锁对象来调用,锁会释放
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }


        }
    }
}
public class text {
    public static void main(String[] args) {
        suodemo suodemo2 = new suodemo();
        Thread thread = new Thread(suodemo2);
        thread.start();
        Thread thread1 = new Thread(suodemo2);
        thread1.start();
    }
}

经典例题:生产者/消费者问题
生产者(Productor)将产品放在柜台(Counter),而消费者(Customer)从柜台
处取走产品,生产者一次只能生产固定数量的产品(比如:1),这时柜台中不能
再放产品,此时生产者应停止生产等待消费者拿走产品,此时生产者唤醒消费者来取走产品,消费者拿走产品后,唤醒生产者,消费者开始等待
ublic class conducter extends Thread{
      Count count;

    public conducter(Count count) {
        this.count = count;
    }

    @Override
    public void run() {
        while (true){
            try {
                count.add();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Count {
   int num = 0;
   public synchronized void add() throws InterruptedException {
       if(num==0){
          System.out.println("生产者生产商品");
          this.num++;
          this.notify();
       }else {
          this.wait();
       }
   }
   public synchronized void quzou() throws InterruptedException {
       if(num==0){
          this.wait();
       }else {
          System.out.println("消费者开始消费商品");
          this.num--;
          this.notify();
       }
   }
}

public class xiaofeizhe extends Thread{
    Count count;

    public xiaofeizhe(Count count) {
        this.count = count;
    }

    @Override
    public void run() {
        while (true){
            try {
                count.quzou();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class text {
    public static void main(String[] args) {
        Count count = new Count();
       new conducter(count).start();
       new xiaofeizhe(count).start();

    }
}

十三.新增创建线程方式

实现Callable接口与使用Runnable相比,Callable功能更强大些.
相比run()方法,可以有返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,获取返回结果接收任务
FutureTask<Integer> futureTask = new FutureTask(任务);
创建线程
Thread t = new Thread(futureTask);
t.start();
Integer val = futureTask.get();获得线程call方法的返回值
代码实现:
public class sum implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int j= 0;
        for (int i = 0; i <5 ; i++) {
            j+=i;
        }
        return j;
    }
}
public class text {
    public static void main(String[] args) {
        sum sum = new sum();
        FutureTask<Integer>futureTask = new FutureTask(sum);//底层实现与runnable有关系
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            Integer integer = futureTask.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值