线程

线程

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

进程:正在执行的程序,加载到内存中执行

线程:进程可进一步细化为线程,是一个进程内部的最小执行单元

进程与线程的关系

一个进程中可以包含多个线程,一个线程只属于一个进程,线程不能脱离进程存在.

一个进程至少包含一个线程,即主线程.例如java中的main就是

所有进程共享进程资源

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

多线程

多线程:在一个进程中,可以拥有多个线程,执行多个任务

何时需要多线程?

程序需要同时执行多个任务

多线程的优点:

同时执行多个任务,功能强大

提高CPU利用率,CPU执行是以线程为单位

改善数据结构,将复杂任务分为多个线程独立运行

多线程的缺点:

线程越多,堆内存,CPU占用越高(处理方法:硬件升级)

多个线程对同一个共享资源进行访问,会出现各种不正常情况

解决方法:线程同步,效率低

创建线程

1.继承Thread类

重写run();方法

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

Test测试

public class Test {
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();//启动线程
        ThreadDemo2 threadDemo2 = new ThreadDemo2();
        threadDemo2.start();//启动线程

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

2.实现Runnable接口

public class ThreadDemo implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println("Thread:"+i);
        }
    }
}

test测试

public static void main(String[] args) {
    //创建一个线程要执行的任务
    ThreadDemo threadDemo = new ThreadDemo();
    //创建线程,并将任务添加到线程中
    Thread t = new Thread(threadDemo);
        t.start();//启动线程
        for (int i = 0; i < 100; i++) {
            System.out.println("main:" + i);
        }
    }

线程中的方法

run();继承线程中需要执行的任务代码

start();启动线程

Tread类中的构造方法

Thread(Runnable target);

Thread(Runnable target , String name);

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

setName();为线程设置名称

getName();获取线程名称

线程优先级

java中线程优先级,用整型1-10

默认优先级为5

 public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();//创建一个线程要执行的任务
        Thread t = new Thread(threadDemo, "自定义线程1");//创建线程,并将任务添加到线程中
        t.setPriority(1);
        t.start();//启动线程
        Thread t1 = new Thread(threadDemo );//创建线程,并将任务添加到线程中
        t1.setName("自定义线程2");//设置线程名称
        t1.setPriority(10);//设置优先级
        t1.start();//启动线程

        for (int i = 0; i < 100; i++) {
            System.out.println("main:" + i);
        }
        System.out.println(t.getPriority());
        System.out.println(t1.getPriority());
        System.out.println(Thread.currentThread().getName());//获取当前正在执行的线程名称
        System.out.println(Thread.currentThread().getPriority());//获取当前线程的优先级
    }
public class ThreadDemo implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

运行结果:

main:0
main:1
自定义线程1:0
自定义线程1:1
自定义线程1:2
自定义线程1:3
自定义线程1:4
自定义线程1:5
自定义线程1:6
自定义线程1:7
自定义线程1:8
自定义线程1:9
自定义线程2:0
自定义线程2:1
自定义线程2:2
自定义线程2:3
自定义线程2:4
自定义线程2:5
自定义线程2:6
自定义线程2:7
自定义线程2:8
自定义线程2:9
main:2
main:3
main:4
main:5
main:6
main:7
main:8
main:9
1
10
main
5

CPU执行策略:

时间片:就是排队,先来先服务

抢占式:对优先级高的先执行

线程的状态

在这里插入图片描述

start();启动线程 就绪状态(等待CPU的加载)

​ 当线程获得了CPU的执行权,进入到运行状态

​ 当线程失去CPU执行权(因为CPU需要轮流执行其他线程),进入待就绪状态

​ 当线程中所有任务执行完以后,进入死亡状态

yield();线程主动让步

sleep();让线程休眠指定时间,进入阻塞状态

t.join();线程合并,等待t进程执行完后,其他进程再执行

stop();强制让线程销毁

public class ThreadDemo extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            /*if (i%5==0){
                Thread.yield();//线程主动让步
            }*/
          /*  try {
                Thread.sleep(1000);//让线程休眠指定时间,进入阻塞状态
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public class Test {
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.setName("线程1");
        threadDemo.start();//启动线程  就绪状态(等待CPU加载)
                           // 当线程获得了CPU的执行权   ,进入到运行状态
                            //当线程失去CPU执行权(因为CPU还要轮流执行其他线程),进入就绪状态
                            //当线程中所有任务执行完以后,进入死亡状态

       /* try {
            threadDemo.join();//线程合并,等待threadDemo线程执行完后,其他线程执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        //threadDemo.stop();//强制让线程销毁
        ThreadDemo threadDemo2 = new ThreadDemo();
        threadDemo2.setName("线程2");
        threadDemo2.start();//启动线程
        //threadDemo2.stop();//强制让线程销毁

    }
}

守护线程

是一种特殊的线程

当把一个线程设置为守护线程时,守护线程需要等所有得非守护线程执行完结束后,才会销毁

t.setDaemon(true);将线程设置为守护线程

JVM中垃圾回收器就是一个典型的守护线程

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


public class Test {
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.setDaemon(true);//将线程设置为守护线程
        threadDemo.start();//启动线程

        for (int i = 0; i < 1000; i++) {
            System.out.println("我是用户线程");
        }
    }
}

线程的同步

并行:多个CPU同时执行多个任务

并发:一个CPU同时执行多个任务

前提:

1.必须是多线程

2.多线程访问同一个共享资源

同步机制:线程排队+锁

static 变量 随着类的加载而加载,生命周期长

非static 变量 生命周期与对象相同

典型案例:买票

票数是固定的,买票的窗口有多个,就有多个线程同时访问同一资源

使用synchronize修饰

通过两种创建线程的方法实现功能

一.继承Thread类

1.synchronize修饰同步代码块

public class VoteDemo extends Thread {

    static int votes = 10;//多个线程访问共享资源
    static Object obj = new Object();//创建唯一的锁


    @Override
    public void run() {
    //while循环一直出票
        while (true) {
        //为需要同步的代码,用同步代码快修饰,多个线程只有一把锁,谁先拿到谁就用
        //当持有锁的线程把同步代码中的内容执行完以后,离开同步代码快,会自动释放锁
            synchronized (obj){
                if (votes > 0) {
                    System.out.println(Thread.currentThread().getName() + ":" + votes);
                    votes--;
                } else if (votes <= 0) {
                    break;
                }
            }
        }
    }

2.用synchronized修饰方法,此方法为同步方法

public class VoteDemo extends Thread {
  static int votes = 10;//多个线程访问共享资源
   @Override
    public void run() {
        while (true) {
            if (votes > 0) {
                vote();
            } else {
                break;
            }
        }
    }
	//用synchronized修饰方法,此方法为同步方法
    //synchronized修饰方式时,锁对象默认为this,多个线程就有多把锁
    public static synchronized void vote() {
        if (votes > 0) {
            System.out.println(Thread.currentThread().getName() + ":" + votes);
            votes--;
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        VoteDemo voteDemo = new VoteDemo();
        voteDemo.setName("窗口1");
        voteDemo.start();

        VoteDemo voteDemo2 = new VoteDemo();
        voteDemo2.setName("窗口2");
        voteDemo2.start();
    }
}

二.实现Runnable类

1.synchronize修饰同步代码块

public class VoteDemo implements Runnable {

    int votes = 10;
    Object obj = new Object();

    //修饰同步内容
    @Override
    public void run() {
        while (true) {
            synchronized (obj){
                if (votes > 0) {

                    System.out.println(Thread.currentThread().getName() + ":" + votes);
                    votes--;
                } else if (votes <= 0) {
                    break;
                }
            }
        }
    }
}

2.用synchronized修饰方法,此方法为同步方法

public class VoteDemo implements Runnable {

    int votes = 10;
    Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            if (votes > 0) {
                Vote();
            } else {
                break;
            }
        }
    }

    //用synchronize修饰方法,此方法为同步方法
    //synchronize修饰方法时,锁对象默认为this,只创建一个线程任务对象,this只有一个
    public synchronized void Vote() {
        if (votes > 0) {
            System.out.println(Thread.currentThread().getName() + ":" + votes);
            votes--;
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        VoteDemo voteDemo = new VoteDemo();
        Thread thread1 = new Thread(voteDemo);
        Thread thread2 = new Thread(voteDemo);
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread1.start();
        thread2.start();
    }
}

线程死锁

当线程持有对方线程中需要的锁时,会出现死锁现象

public class DieLock extends Thread {

    static Object objA = new Object();
    static Object objB = new Object();

    boolean flag;


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

    @Override
    public void run() {
        if (flag){
            synchronized (objA){
                System.out.println("if objA");//objA这把锁已经被使用
                synchronized (objB){//此时只能等待下面释放objB锁
                    System.out.println("if objB");
                }
            }
        }else{
            synchronized (objB){
                System.out.println("else objB");//objB这把锁也被使用
                synchronized (objA){//此时等待上面释放objA锁
                    System.out.println("else objA");
                }
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        DieLock t1= new DieLock(true);
        DieLock t2= new DieLock(false);

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

运行结果:

死锁出现不会报错,不会提示,只是等待.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DZyhn87e-1611997903680)(C:\Users\17509\AppData\Roaming\Typora\typora-user-images\1611204001419.png)]

Lock方式的加锁

JDK5之后使用Lock方式显示的加锁,释放锁

Lock是显式锁(手动开启和关闭锁,别忘记关闭锁), 实现类ReentrantLock
synchronized是隐式锁,出了作用域自动释放

Lock只有代码块锁

synchronized 有代码块锁,方法

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class VoteDemo extends Thread {

    static int num = 10;
    static Lock lock = new ReentrantLock();//创建锁对象
    @Override
    public void run() {
        while(true){

            try {
                lock.lock();//获得锁,加锁
                Thread.sleep(100);
                if(num>0){
                    System.out.println(num);
                    num--;
                }else{
                    break;
                }
                lock.unlock();//释放锁
            }catch (Exception e){
                e.printStackTrace();
                lock.unlock();//释放锁
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        VoteDemo voteDemo = new VoteDemo();
        voteDemo.setName("窗口1");
        voteDemo.start();

        VoteDemo voteDemo2 = new VoteDemo();
        voteDemo2.setName("窗口2");
        voteDemo2.start();
    }
}

线程通信

就是多个线程之间相互牵制,相互调度执行

wait();让线程等待,进入阻塞状态,不会自动唤醒

notify();唤醒等待的线程,如果有多个,那就唤醒优先级高的

notifyAll();唤醒所有等待的线程

案例:交替打印1-100的数

1.使用lock锁,实现Runnable的方法

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class PrintNum implements Runnable {

    static int num = 0;
    static Lock lock = new ReentrantLock();
    static Condition con = lock.newCondition();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                con.signal();//唤醒线程
                num++;
                if (num <= 100) {
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                } else {
                    lock.unlock();
                    break;
                }
                con.await();//线程等待,并释放锁
                lock.unlock();
            } catch (InterruptedException e) {
                e.printStackTrace();
                lock.unlock();
            }
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        PrintNum p =new PrintNum();
        Thread p1 = new Thread(p);
        Thread p2 = new Thread(p);
        p1.start();
        p2.start();
    }
}

2.实现Runnable的方法,使用synchronize修饰同步代码块

public class PrintNum implements Runnable{

    static int num=0;
    @Override
    public void run() {
        while (true){
            synchronized (this){//implements Runnable 情况下,this只有一个,this可以充当锁
                this.notify();//唤醒等待线程 使用锁对象来调用notify(),wait();
                if (num<=100){
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    num++;
                }else{
                    break;
                }
                try {
                    this.wait();//线程等待,并释放锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        PrintNum p =new PrintNum();
        Thread p1 = new Thread(p);
        Thread p2 = new Thread(p);
        p1.start();
        p2.start();
    }
}

3.继承Thread类的方法,使用synchronize修饰同步代码块

public class PrintNum extends Thread {

    static int num = 0;
    static Object obj =new Object();//继承Thread方式,由于创建了多个对象,需要自己创建一个唯一的对象,充当锁
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                obj.notify();
                if (num <= 100) {
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    num++;
                } else {
                    break;
                }
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        PrintNum p1 = new PrintNum();
        PrintNum p2 = new PrintNum();
        p1.start();
        p2.start();
    }
}

消费者生产者问题

1.柜台类,存放物品的类

/*柜台中 存放共享数据*/
public class Counter {
    int num = 0;//初始物品数量为0

    /* 生产商品*/
    public synchronized void add() {
        if (num == 0) {
            num++;
            System.out.println("生产一个");
            this.notify();//唤醒消费者线程  this表示同一个柜台
        } else {
            try {
                this.wait();//生产者等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /*消费商品 */
    public synchronized void sub() {
        if (num == 1) {
            num--;
            System.out.println("消费一个");
            this.notify();//唤醒生产者线程  this表示同一个柜台
        } else {
            try {
                this.wait();//消费者等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.生产者线程

public class Productor extends Thread {

    Counter c;

    public Productor(Counter c) {
        this.c = c;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);//休眠指定的时间
                c.add();//调用add方法
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3.消费者线程

/*消费者线程*/
public class Customer extends Thread {

    Counter c;

    public Customer(Counter c) {
        this.c = c;
    }

    @Override
    public void run() {

        while (true) {
            try {
                Thread.sleep(1000);
                c.sub();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

4.测试类

public class Test {

    public static void main(String[] args) {

        Counter c = new Counter();//创建柜台对象,是生产或者和消费者
        Productor p = new Productor(c);
        Customer ct = new Customer(c);
        p.start();
        ct.start();
    }
}

jdk8以后的两种创建线程的方法

以1-100求和问题

1.FutureTask用来接受任务

import java.util.concurrent.Callable;

public class SumNumThread implements Callable<Integer> {
    /*
    可以向外界返回一个结果
    可以抛出一个异常
     */
    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 1; i <= 100;i++){
            sum+=i;
        }
        return sum;
    }
}
public class Test {
    public static void main(String[] args) {
        //创建任务
        SumNumThread sum = new SumNumThread();
        //接受任务
        FutureTask<Integer> futureTask = new FutureTask(sum);
        //创建线程
        Thread t = new Thread(futureTask);

        t.start();
        try {
            Integer val = futureTask.get();
            System.out.println(val);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

2.ExecutorService创建线程池

public class SumNumThread implements Callable<Integer> {
    /*
    可以向外界返回一个结果
    可以抛出一个异常
     */
    @Override
    public Integer call() throws Exception {
        int sum=0;
        System.out.println(Thread.currentThread().getName());
        for (int i = 1; i <= 100;i++){
            sum+=i;
        }
        return sum;
    }
}
public class Test {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        SumNumThread sumNumThread = new SumNumThread();

        //将任务交给线程池中线程执行
        //线程池中线程不够时,会释放其中的重复利用
        executorService.submit(sumNumThread);
        executorService.submit(sumNumThread);
        executorService.submit(sumNumThread);
        executorService.submit(sumNumThread);
        executorService.submit(sumNumThread);
        Future<Integer> f = executorService.submit(sumNumThread);
        try {
            System.out.println(f.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        executorService.shutdown();
    }
}
posted @ 2021-12-03 17:21  肖帆咪  阅读(28)  评论(0编辑  收藏  举报