线程(多线程间影响、线程同步、锁...)

    • 程序、进程、线程

● 程序:一段静态的代码,在电脑安装的静态代码文件。

● 进程:运行中的程序,是操作系统进行资源分配的最小单位。

● 线程:进程中最小的执行单位,cpu以线程为单位调度线程。

● 进程与线程的关系:

一个进程包含多个线程,一个线程只属于一个进程;

线程不能脱离进程独立运行;

一个进程中至少有一个线程,即主线程

2.创建线程

2.1继承Thread类

● 代码语法:

public class MyThread extends Thread{
    //需独立在线程中执行的任务,写在run();中

    @Override
    public void run() {
       
    }
}
public class Test {
    //顺序由操作系统决定
    public static void main(String[] args) {
    
        MyThread myThread=new MyThread();//创建线程对象
        myThread.start();//启动线程
    }
}

2.2实现Runnable接口

● 代码语法:

public class MyThread implements Runnable{

    @Override
    public void run() {
        
    }
}
public class Test {
    public static void main(String[] args) {
        
        //创建线程中需执行的任务,没有创建线程
        MyThread myThread=new MyThread();

        //创建线程,添加任务
        Thread thread=new Thread(myThread);
        thread.start();
        
    }
}

● 好处:

1. 可多继承其他类

2.适合多线程处理同一份资源

3.Thread类中方法

4.线程优先级

优先级以整数表示,范围是:1~10,一般线程的默认优先级为5,可通过setPriority()和getPriority()来设置,返回优先级。

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

Thread类有如下3个静态常量来表示优先级 ● MAX_PRIORITY:取值为10,表示最高优先级。 ● MIN_PRIORITY:取值为1,表示最底优先级。 ● NORM_PRIORITY:取值为5,表示默认的优先级。

5.线程状态

● 新建:当一个Thread类或其子类的对象被声明并创建时,此时不能执行;

● 就绪:处于新建状态的线程被start()后,进入线程队列等待,此时可执行;

● 运行:当就绪的线程被调度且获得CPU执行权时,进入运行状态;

● 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,失去cpu执行权,临时中止自己的执行,进入阻塞状态

● 死亡:线程完成了它的全部工作/线程被提前强制性地中止/出现异常 导致结束。

6.守护线程

● Java中线程分为:用户线程和守护线程。

● 守护线程为其他线程的执行提供服务,只要当前JVM中存在非守护线程未结束,守护线程就全部运行。

● 二者的区别:若用户线程全部退出运行,守护线程会随之结束运行。

● 设置守护线程:

setDaemon(boolean on)

设置线程为守护线程要在启动线程前,否则会出现IllegalThreadStateException异常。

public class Test {
    public static void main(String[] args) {
        ThreadDemo t=new ThreadDemo();
        t.start();
        DaemonThread daemonThread=new DaemonThread();
        daemonThread.setDaemon(true);//必须写在线程启动前,设置线程为守护线程
        daemonThread.start();
    }
}


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

public class DaemonThread extends Thread{
    @Override
    public void run() {
        while(true){
            System.out.println("DaemonThread");
        }
    }
}

7.多线程

7.1概念

指允许单个程序可以同时运行多个不同的线程来执行不同的任务。

7.2优缺点

● 优点:

提高CPU利用率;

提高程序响应;

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

● 缺点:

内存随线程的增多而增大;

线程之间访问共享资源会受影响。

8.线程同步

8.1并发、并行

● 并发:一个时间段内一次执行操作。如:买票,看似同时进行,实则一个个进行。

● 并行:多个CPU同时执行多个任务。如:多个人同时做不同事。

8.2多线程同步

多线程同时共享同一份资源,会产生影响。故由线程”同步“来解决此问题。

8.3同步锁

可以是任何对象,但必须唯一,使得多个线程访问同一对象。

● 同步执行过程:

  1. 第一个线程访问,锁定同步对象,执行代码;

  1. 第二个线程访问,同步对象被锁定,无法访问;

  1. 第一个线程访问完毕,解锁同步对象;

  1. 第二个线程访问,锁定并访问。

Java中实现同步:

    • 使用synchronized关键字修饰

1.1修饰代码块

synchronized(键对象【可以为任何对象】){

必须多个线程对应同一个对象,

用该对象记录是否由线程进入到同步代码块中,

用该对象的对象头中的一块空间记录锁的状态。

}

//1.当继承Thread时
public class TicketThread extends Thread{
    static int num=10;//内存中只有一份,两线程对象共用同一份
    static String s=new String();//确保多个线程对应同一对象
    @Override
    public void run() {
        while(true){
            synchronized(s){
                if(num>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"买到了"+num--);
                }
                else{
                    break;
                }
            }
        }
    }
}


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();
    }
}

//2.当实现Runnable接口时
public class Ticket implements Runnable{
    int num=10;
    @Override
    public void run() {
        while(true){
            synchronized (this){//只创建了一个对象,仅有一个this
                if(num>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"买到了"+num--);
                }
                else{
                    break;
                }
            }
        }
    }
}


public class Test {
    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        Thread t1=new Thread(ticket,"1号");
        t1.start();
        Thread t2=new Thread(ticket,"2号");
        t2.start();
    }
}

1.2修饰方法
//1.当继承Thread时
public class TicketThread extends Thread{
    static int num=10;//内存中只有一份,两线程对象共用同一份
    static String s=new String();//确保多个线程对应同一对象
    @Override
    public void run() {
        while(true){
            if(num<=0){
                break;
            }
            TicketThread.printTicket();
        }
    }
    /*
    synchronized修饰非static方法时,键对象是this,可能有多个this,
    若修饰静态方法  staitc ,锁对象变为该类的class类的对象.
     */
    public  static synchronized void printTicket() {
        if(num>0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"买到了"+num--);
        }
    }
}


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();
    }
}
//2.当实现Runnable接口时
public class Ticket implements Runnable{
    int num=10;
    @Override
    public void run() {
        while(true){
            if(num<=0){
                break;
            }
            this.printTicket();
        }
    }
    public synchronized void printTicket(){
        if(num>0){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"买到了"+num--);
        }

    }
}

public class Test {
    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        Thread t1=new Thread(ticket,"1号");
        t1.start();
        Thread t2=new Thread(ticket,"2号");
        t2.start();
    }
}

9.Lock锁

● 使用ReentrantLock类实现Lock,可以显示加锁,释放锁。

Lock lock=new ReentrantLock();
/*创建ReentrantLock对象,当有线程获取执行权时,底层state状态由0变为1
    若还有其他线程访问,都会进入到一个队列中等待
*/


lock.lock();//加锁

lock.unlock();//释放锁,一般写在finally代码块中,以防出现异常不能执行

10.线程死锁

● 多个线程分别占用对方所需的资源不放手,都在等待对方放弃自己所需的同步对象。程序不报错,也不提示。称为死锁

● 代码举例:

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");
            }
            synchronized (objB){
                System.out.println("if objB");
            }
        }
        else{
            synchronized (objB){
                System.out.println("else objB");
            }
            synchronized (objA){
                System.out.println("else objA");
            }
        }
    }
}



public class Test {
    public static void main(String[] args) {
        DieLock d1=new DieLock(true);
        DieLock d2=new DieLock(false);
        d1.start();
        d2.start();
    }
}

11.线程通信

11.1概念

● 多个线程通过相互牵制,相互调度,即线程间的相互作用。

11.2三方法

    wait();   在Object类 中定义,使线程进入等待状态,
             须通过notify() 进行唤醒,可自动释放锁对象。
    notify(;)  在Object类 中定义,唤醒等待中的线程
    notifyAll();  唤醒所有等待的线程
    以上方法,必须在同步代码块/同步方法中使用

11.3例题:生产者/消费者

假设生产者(Product)每次在柜台(Counter)上生产固定数量的商品(如:1个),且柜台不能再放商品,此时生产者停止生产,进入等待状态,来唤醒消费者消费,而消费者消费后,唤醒生产者,再进入等待状态。

public class Counter {
    int num=0;

    public synchronized void add(){
        if(num>0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        else{
            System.out.println("生产者生产");
            num+=1;
            this.notify();
        }
    }
    public synchronized void sub(){
        if(num==0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        else{
            System.out.println("消费者消费");
            num-=1;
            this.notify();
        }

    }
}
public class Product extends Thread{

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

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter.add();
        }
    }
}
public class Customer extends Thread{

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

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter.sub();
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Counter counter=new Counter();
        Product product=new Product(counter);
        Customer customer=new Customer(counter);
        product.start();
        customer.start();
    }
}

12.新增创建线程方式

● 实现Callable接口:

• 相比run()方法,可以有返回值 • 方法可抛出异常 • 支持泛型的返回值 • 使用FutureTask类,获取返回结 果

//接收任务
FutureTask<Integer> futureTask = new FutureTask(任务);
//创建线程
Thread t = new Thread(futureTask);
t.start();
Integer val = futureTask.get();获得线程call方法的返回值

● 代码举例:

import java.util.concurrent.Callable;

public class Fun implements Callable {

    @Override
    public Integer call() throws Exception {
        int num=0;
        for (int i = 0; i < 100; i++) {
            num+=i;
        }
        return num;
    }
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Fun fun=new Fun();
        FutureTask<Integer> futureTask=new FutureTask<>(fun);
        Thread t=new Thread(futureTask);
        t.start();
        Integer s=futureTask.get();
        System.out.println(s);//4950
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小谭同学ha

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值