JavaThread线程

目录

程序--进程--线程

程序:

进程:

线程:

进程和线程的关系

创建线程 

单线程模式

代码

图解

运行

代码

运行 

创建线程

方式一

代码

运行 

方式二

代码

运行

总结:

Thread类中常用的方法

1、

代码 

运行 

2、

代码 

运行 

3、

代码 

运行

4、

代码 

运行

5、

代码 

运行

​编辑

线程优先级 

操作系统线程任务调度算法

线程状态

新建 

就绪

运行

阻塞

死亡

守护线程

代码

运行

多线程的概念

何时需要多线程

多线程的优点

多线程的缺点

线程同步

多线程同步

加锁方式1:

Thread类

修饰代码块

代码 

运行

修饰方法

代码

运行

Runnable类

修饰代码块

代码

运行

修饰方法

代码

运行

加锁方式2:

代码

运行

synchronized 与 ReentrantLock区别

相同点:

不同点:

线程通信

代码 

运行

新增创建线程方式

代码

运行


程序--进程--线程

程序:

为解决某种问题,通过计算机语言编写的一系列指令(代码)的集合。

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

进程:

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

线程:

进程可以进一步细化为线程,是进程内最小的执行单位(具体要做的事情),是cpu进行任务调度的最小单位 。

运行中的QQ就是一个进程,操作系统会为这个进程分配内存资源,一个聊天窗口就认为是一个线程,这多个聊天窗口可以同时被cpu执行,但是这些聊天窗口属于进程,线程是属于进程的。

早期没有线程,早期cpu在执行时是以进程为执行单位的,进程单位还是比较大的,当一个进程运行时,其他的进程就不能执行,所以后来,将进程中的多个任务,细化为线程。cpu执行单位,也从进程转为更小的线程。

进程和线程的关系

1、一个进程可以包含多个线程;

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

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

4、在主线程可以创建并启动其他线程;

5、所以线程都共享进程的内存资源。

创建线程 

单线程模式

代码

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);
        }
        for (int i = 0; i < 10; i++) {
            System.out.println("循环2:" + i);
        }
    }
}

图解

运行

代码
package com.ffyc.javathread.demo1;

public class Demo2 {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println("循环1:" + i);
        }

        test();

        System.out.println("最后的代码");
    }
    public static void test(){
        for (int i = 0; i < 10; i++) {
            System.out.println("循环2:" + i);
        }
    }

}

运行 

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

可以在java中创建线程,把一些要执行的任务放在线程中执行,这样的话,都拥有让cpu执行的权力 

创建线程

方式一

MyThread 继承 Thread(线程)
重写Thread类中run()方法,在run()方法中来编写我们需要执行的任务代码 

调用start()启动线程  

代码
package com.ffyc.javathread.demo1; 
public class MyThread extends Thread{

    //run方法是线程执行任务的方法
    @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("Test"+i);
        }
    }
}
运行 

方式二

创建一个任务,实现Runnable接口,把这个类不能称为线程,是一个任务类
重写Runnable接口中的run方法

代码
package com.ffyc.javathread.demo2; 
public class MyTask implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("MyTask:"+i);
        }

    }
}

main方法是用来启动java主线程的 

package com.ffyc.javathread.demo2;

public class Test { 
    public static void main(String[] args) {

        //创建一个任务的对象
        MyTask myTask = new MyTask();

        //真正意义上的创建了一个线程
        Thread thread = new Thread(myTask);
        //启动线程,去执行mytask任务
        thread.start();

        for (int i = 0; i < 10000; i++) {
            System.out.println("main:"+i);
        }
    }
}
运行

总结:

两种方法后期使用率上,第二种相对更高一些

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

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

Thread类中常用的方法

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

1、

run();线程要执行的任务在run方法中进行定义

start();启动java线程的

构造方法

new  Thread(Runnable  runnable);接收一个任务对象

代码 

package com.ffyc.javathread.demo1; 
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(); 
        myThread.start();

        for (int i = 0; i < 10000; i++) {
            System.out.println("Test"+i);
        }
    }
}

运行 

2、

new  Thread(Runnable  runnable,String  name);接收一个任务对象,并为线程设置名字

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

String  getName();获得线程的名字

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

代码 

package com.ffyc.javathread.demo2;

public class MyTask_Name implements Runnable{
    @Override
    public void run() {
        //在任务中,我想知道当前是那个线程正在执行
        //在任务中,通过currentThread()获得当前正在执行的线程
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
    }
}
package com.ffyc.javathread.demo2;

public class Test_Name {
    public static void main(String[] args) {

        MyTask_Name myTask_name = new MyTask_Name();

        Thread thread1 = new Thread(myTask_name,"线程1");
        thread1.setName("我的线程1");
        thread1.start();

        //又创建了一个线程,把同一个任务交给线程去执行
        Thread thread2 = new Thread(myTask_name);
        thread2.start();
    }
}

运行 

3、

setPriority(int p);设置优先级  1-10之间  默认是5

getPriority();获得优先级

代码 

package com.ffyc.javathread.demo3;

public class MyTask implements Runnable{
    @Override
    public void run() {
        //获得当前正在执行对象
        Thread thread = Thread.currentThread();
        //获得线程优先级
        System.out.println(thread.getName()+":"+thread.getPriority());

    }
}
package com.ffyc.javathread.demo3;

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");

        //设置线程优先级  1-10之间  默认是5
        t1.setPriority(8);
        t2.setPriority(2);

        t1.start();
        t2.start();

        System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());
    }

}

运行

4、

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

代码 

package com.ffyc.javathread.demo3;

public class MyTask implements Runnable{
    @Override
    public void run() {
        //获得当前正在执行对象
        Thread thread = Thread.currentThread();
        if (thread.getName().equals("线程1")) {
            try {
                //让线程休眠 1000毫秒
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //获得线程优先级
        System.out.println(thread.getName() + ":" + thread.getPriority());
    }
}
package com.ffyc.javathread.demo3;

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");

        //设置线程优先级  1-10之间  默认是5
        t1.setPriority(8);
        t2.setPriority(2);

        t1.start();
        t2.start();

        System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());
    }

}

运行

5、

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

代码 

package com.ffyc.javathread.demo3;

public class MyTask implements Runnable{
    @Override
    public void run() {
        //获得当前正在执行对象
        Thread thread = Thread.currentThread(); 
        //获得线程优先级
        System.out.println(thread.getName() + ":" + thread.getPriority());
    }
}
package com.ffyc.javathread.demo3;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyTask myTask = new MyTask();
        Thread t1 = new Thread(myTask,"线程1");
        Thread t2 = new Thread(myTask,"线程2"); 

        t2.start();
        //让其他线程等待当前线程结束后再执行
        t2.join();
        t1.start();

        System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());
    }

}

运行

线程优先级 

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

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

优先级用整数表示,取值范围是1~10,一般情况下,线程的默认优先级 都是5,但是也可以通过setPriority和getPriority方法来设置或返回优 先级;

操作系统线程任务调度算法

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

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

优先级调度算法

时间片轮转调度算法

高响应比优先级调度算法

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

线程状态

线程再它的生命周期中会处于不同的状态;

线程生命周期,线程从创建到销毁,期间经历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开销变大;

线程之间同时对共享资源的访问会相互影响,如果不加以控制会导致数据 出错.

线程同步

确保一个时间点只有一个线程访问共享资源。可以给共享资源加一把锁,哪个 线程获取了这把锁,才有权利访问该共享资源。

多线程同步

多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线程“同步”机制, 即各线程间要有先来后到;

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

加锁方式1:

多个线程访问同一个共享的数据,如果不加以控制,在理论上就会出现问题

Thread类

使用synchronized关键字

修饰代码块

同步对象要求:

锁对象,必须多个线程对应的是同一个对象,可以是java中任何类的对象

作用:可以记录有没有线程进入到同步代码块中

synchronized (锁对象){ 
         使用锁对象的对象头中的一块空间来记录锁的状态
         同步代码块
}  

代码 
package com.ffyc.javathread.demo5;

public class TicketThread extends Thread{
    String name; 
    //静态变量,在内存中只有一份,两个线程对象共用同一个
    static int num = 10;
    //必须确保多个线程对应的是同一个对象即可
    static String s = new String();

    @Override
    public void run() {
        while (true){
            synchronized (s){
                if(num > 0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"买到了:"+num--);
                }else{
                    break;
                }
            }
        }
    }
}
package com.ffyc.javathread.demo5;

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

        t1.start();
        t2.start();
    }
}
运行

修饰方法

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

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

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

      一个类只有一个Class类对象

代码
package com.ffyc.javathread.demo5;

public class TicketThread extends Thread{
    String name;
    //静态变量,在内存中只有一份,两个线程对象共用同一个
    static int num = 10; 

    @Override
    public void run() {
        while (true){
            if(num <= 0){
                break;
            }
            TicketThread.printTicket();
        }
    } 
    public static synchronized void printTicket(){
        if(num > 0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"买到了:"+num--);
        }
    }
}
package com.ffyc.javathread.demo5;

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

        t1.start();
        t2.start();
    }
}
运行

Runnable类

修饰代码块
代码
package com.ffyc.javathread.demo6;

import com.ffyc.javathread.demo5.TicketThread;

public class Ticket implements Runnable{
    //对于多个线程来说,票数只有一份
    int num = 10;
    @Override
    public void run() {
        while (true){
            //this只有一个
            synchronized (this){
                if(num > 0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"买到了:"+num--);
                }else{
                    break;
                }
            }
        }
    }
}
package com.ffyc.javathread.demo6;

public class Test {

    public static void main(String[] args) {
        //创建了一个出票的任务
        Ticket ticket = new Ticket();

        Thread t1 = new Thread(ticket,"窗口1");
        Thread t2 = new Thread(ticket,"窗口2");
        t1.start();
        t2.start();
    }
}
运行

修饰方法
代码
package com.ffyc.javathread.demo6;

import com.ffyc.javathread.demo5.TicketThread;

public class Ticket implements Runnable{
    //对于多个线程来说,票数只有一份
    int num = 10;
    @Override
    public void run() {
        while (true){
            if(num <= 0){
                break;
            }
            printTicket();
        }
    }

    public synchronized void printTicket(){
        if(num > 0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"买到了:"+num--);
        }
    }
}
package com.ffyc.javathread.demo6;

public class Test {

    public static void main(String[] args) {
        //创建了一个出票的任务
        Ticket ticket = new Ticket();

        Thread t1 = new Thread(ticket,"窗口1");
        Thread t2 = new Thread(ticket,"窗口2");
        t1.start();
        t2.start();
    }
}
运行

加锁方式2:

使用jdk中提供的ReentrantLock类实现加锁

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

代码
package com.ffyc.javathread.demo7;

import java.util.concurrent.locks.ReentrantLock;

public class TicketTask implements Runnable{

    int num = 10;
    //提供一个实现加锁的对象
    ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            //加锁
            reentrantLock.lock();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                if(num > 0){
                    System.out.println(Thread.currentThread().getName()+":买到了"+num--);
                }else {
                    break;
                }
            }finally {//在finally块中保证锁必须释放
                //释放锁
                reentrantLock.unlock();
            }
        }
    }
}
package com.ffyc.javathread.demo7;

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

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

运行

synchronized 与 ReentrantLock区别

相同点:

都实现了加锁的功能

不同点:

1、synchronized是一个关键字,ReentrantLock是一个类

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

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

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

线程通信

线程间的通信(在同步代码块的基础上,使用wait,notify来对线程进行控制) 

      wait(); notify();  notifyAll();
      这三个方法都是Object类中定义的方法
      这三个方法必须在同步代码块中使用
      这三个方法必须通过为锁的对象调用

代码 

package com.ffyc.javathread.demo8;

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

    @Override
    public void run() {
         while (true){
              synchronized (o){
                  //唤醒等待中的线程
                  o.notify();
                  if(num < 100){
                      System.out.println(Thread.currentThread().getName()+":"+ ++num);
                  }else {
                      break;
                  }
                  try {
                      //让线程等待,同时释放锁,等待的线程不能自己醒来,必须让另一个线程唤醒
                      o.wait();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
         }
    }
}
package com.ffyc.javathread.demo8;

public class Test {
    public static void main(String[] args) {
        PrintNumThread p1 = new PrintNumThread();
        p1.setName("线程1");
        PrintNumThread p2 = new PrintNumThread();
        p2.setName("线程2");

        p1.start();
        p2.start();
    }
}

运行

......

新增创建线程方式

实现Callable接口,重写call()方法

call()可以有返回值,可以抛出异常,还可以自定义返回结果的类型

代码

package com.ffyc.javathread.demo10;

import java.util.concurrent.Callable;

public class SumTask<T> implements Callable<T> {

    @Override
    public T call() throws Exception {
         Integer i = 0;
        for (int j = 0; j < 10; j++) {
            i+=j;
        }
        return (T)i;
    }
}
package com.ffyc.javathread.demo10;

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

public class Test {
    public static void main(String[] args) {
        SumTask<Integer> sumTask = new SumTask<>();
        FutureTask<Integer> futureTask = new FutureTask(sumTask);
        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();
        }
    }
}

运行

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值