多线程

多线程

程序(静态)

一段静态的代码

进程(动态)

正在运行的一个程序

自身的产生、存在和消亡–生命周期

进程作为资源分配的单位

线程

进程可细化为线程–一条执行路径

线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器

内存:一个进程占用独立的方法区和堆,一个线程占用独立的虚拟机栈和程序计数器,多个线程共享同一进程的方法区和堆的内存

单核CPU与多核CPU:前者时间单元短,后者发挥多线程效率高

并行与并发

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

并发:一个CPU采用时间片同时执行多个任务

线程的创建(继承Thread类)

/**
1.创建一个继承于Thread类的子类
2.重写Thread类的run()
3.创建Thread类的子类对象
4.通过该对象调用start()
*/
public class ThreadTest {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
    }
}

class  MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            if(i%2==0){
                System.out.println(i);
            }
        }
    }
}

start()的作用:启动当前线程;调用当前线程的run()方法,而且每个线程的start()只能使用一次

线程的常用方法

void start();

run();一般需要重写

String getName();获取线程名字

void setName(String name);设置线程名字

static Thread currentThread();获取当前线程

static void yield();让步,释放当前CPU的执行

join();在线程A中调用线程B的join方法,此时线程A进入阻塞状态,直到线程B完成

static void sleep(long millistime);让当前线程在指定的时间内进入阻塞状态

isAlive();判断当前线程是否存活

线程的调度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zcFgfzPt-1600600699144)(C:\Users\wade\Desktop\后端开发\线程调度.jpg)]

1.同优先级线程组成先进先出队列,使用时间片策略

2.对高优先级,使用优先调度的抢占式策略

优先级1-10,默认为5

getPriority();获取当前线程优先级

setPriority();设置当前线程优先级

线程创建时继承父类线程优先级

低优先级只是获得调度的概率低,并不是在高优先级完成后执行

线程的创建(实现Runnable接口)

/**
 * 实现Runnable接口
 * 1.创建一个实现Runnable接口的类
 * 2.实现类去实现Runnable接口的抽象方法run()
 * 3.创建实现类的对象
 * 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
 * 5.调用Thread类的对象的start方法
 */
public class RunnableTest {
    public static void main(String[] args) {
        RunnableDemo r1=new RunnableDemo();
        new Thread(r1).start();
    }

}
class RunnableDemo implements Runnable{
    private static int nums=100;
    @Override
    public void run() {
        while(nums>0){
            System.out.println(nums);
            nums--;
        }
    }
}

两种方式的比较

类已有Thread以外的父类,应使用Runnable接口(开发中优先选择)

Runnable接口:没有单继承的局限性;可共享资源

线程的安全问题

当一个线程在操作共享数据时,其他线程不能参与进来,直到线程a操作万其他线程才能获取权限,即使线程a出现了阻塞也不能改变。

通过同步机制解决线程的安全问题

同步机制

共享数据:多个线程共同操作的变量

1.同步代码块

synchronized(同步监视器){

//需要被同步的代码,用于操作共享数据的代码

}

同步监视器:俗称锁,任何一个类的对象(多个线程必须共用一把锁)都可以作为锁

//使用同步代码块解决了实现Runnable接口的类的线程安全问题
class RunnableDemo implements Runnable{
    private int nums=100;
    private Object obj=new Object();
    @Override
    public void run() {
        while(true) {
            synchronized (obj) {//可以用this
                if (nums > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + nums);
                    nums--;
                } else break;
            }
        }
    }
}
//使用同步代码块解决了继承Thread类的类的线程安全问题
class window extends Thread{
    private static int nums=100;
    private static Object obj=new Object();
    public window(String name) {
        super(name);
    }

    @Override
    public void run() {
        while(true) {
            synchronized(obj){
                if(nums>0) {
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName() + ":" + nums);
                    nums--;
                }
                else break;
        }
        }
        }

}
2.同步方法

如果操作共享数据的代码完整的声明在一个方法中,将此方法声明为同步的

非静态的同步方法的同步监视器是this

静态的同步方法的同步监视器是该方法所在的类class

//使用同步方法解决了实现Runnable接口的类的线程安全问题
class RunnableDemo implements Runnable{
    private int nums=100;
    private Object obj=new Object();
    @Override
    public void run() {
        while(show()) {

            }
        }

    private synchronized boolean show(){//监视器是this

            if (nums > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + nums);
                nums--;
                return true;
            }
            else return false;

    }
}
//使用同步方法解决了继承Thread类的类的线程安全问题
class window extends Thread{
    private static int nums=100;
    private static Object obj=new Object();
    public window(String name) {
        super(name);
    }

    @Override
    public void run() {
        while(true) {
            show();
        }
        }

    private static synchronized void show(){//必须是静态
        synchronized(obj){
            if(nums>0) {
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":" + nums);
                nums--;
            }

        }
    }

}
3.线程的死锁问题

不同的线程分别占用对方需要的同步资源不放手

虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件。

1)**互斥条件:**指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。

2)**请求和保持条件:**指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3)**不剥夺条件:**指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4)**环路等待条件:**指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

同步锁(LOCK)

使用ReentrantLock类实现Lock

public class ReentrantLockTest {
    public static void main(String[] args) {
        window w1=new window();
        Thread t1=new Thread(w1);
        Thread t2=new Thread(w1);
        Thread t3=new Thread(w1);

        t1.setName("一");
        t2.setName("二");
        t3.setName("三");
        t1.start();
        t2.start();
        t3.start();
    }
}

class window implements Runnable{
    private int ticks=100;
    //第一步实例化
    private ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
              while(true){
                  try{
                      //2.调用lock方法
                      lock.lock();
                      if(ticks>0){
                          System.out.println(Thread.currentThread().getName()+ticks);
                          ticks--;
                      }
                      else break;
                  }
                  finally {
                      //3.调用unlock方法
                      lock.unlock();
                  }

              }
    }
}
异同

Lock是手动的,使用lock方法(上锁)和unlock方法(解锁),相对灵活;

synchronized是自动的,使用到同步监视器,自动地释放同步监视器

线程的通信

使用wait()方法使得调用该方法的线程进入阻塞状态且会释放锁

使用notify()|notifyall()唤醒阻塞的线程

public class CommunicationTest {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1=new Thread(number);
        Thread t2=new Thread(number);
        t1.setName("线程一");
        t2.setName("线程二");
        t1.start();
        t2.start();
    }
}

class Number implements Runnable{
    private int number=1;
    @Override
    public void run() {
        while(true){
            synchronized (this) {
                notify();
                if(number<=100){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+number);
                    number++;
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                else break;
            }
        }
    }
}

wait(): 当前线程进入阻塞状态,并释放同步监视器

notify(): 唤醒被wait()的一个线程,优先级高先被唤醒

notifyAll(): 唤醒所有被wait()的线程

以上三个方法必须使用在同步代码块或同步方法中,且从同步监视器中调用,他们定义在Object类中

sleep()和wait()的异同

相同:让线程进入阻塞状态

不同:1.两个方法声明位置不同,前者是在Thread类中,后者在Object类中

2.前者不释放锁,后者释放锁

3.前者可以在任何需要的地方使用,后者必须在同步代码块或同步方法中

生产者与消费者例题

线程的创建(实现Callable接口)

/**
 * 1.创建实现Callable接口的实现类
 * 2.将call()方法实现,可有返回值和抛出异常
 * 3.创建Callable接口实现类的对象
 * 4.将此对象传递到FutureTask构造器中,创建FutureTask对象
 * 5.将FutureTask对象传递到Thread类的构造器中,创建Thread类并调用start方法
 * 6.如需返回值,可使用FutureTask的get函数
 */
public class ThreadNew {
    public static void main(String[] args) {
        NumThread numThread=new NumThread();

        FutureTask futureTask = new FutureTask(numThread);

        new Thread(futureTask).start();

        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}
class NumThread implements Callable{

    @Override
    public Object call() throws Exception {
        int sum=0;
        for(int i=0;i<100;i++){
            if(i%2==0){
                System.out.println(i);
                sum+=i;
            }
        }
        return sum;

    }
}

Callable的优势之处:有返回值、可抛出异常、支持泛型

线程的创建(使用线程池)

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

class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<50;i++){
            if(i%2!=0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

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

        //1.提供指定线程数量的线程池
        ExecutorService Service = Executors.newFixedThreadPool(10);
        //2.执行指定线程的操作。需要提供实现Runnable接口或Callable接口的类的对象
        Service.execute(new NumberThread());//适合使用于Runnable
        Service.execute(new NumberThread1());//适合使用于Runnable
        //3.关闭线程池
        Service.shutdown();
        //Service.submit();//适合使用于Callable

    }
}

优点:提高响应速度(减少了创建线程的时间)、降低资源的消耗(重复利用线程池线程)、便于线程管理(corePoolSize池大小、maximumPoolSize最大线程数、keepAliveTime线程空置最大时长)

创建多线程有四种方式

1.继承Thread类

2.实现Runnable接口

3.实现Callable接口

    ExecutorService Service = Executors.newFixedThreadPool(10);
    //2.执行指定线程的操作。需要提供实现Runnable接口或Callable接口的类的对象
    Service.execute(new NumberThread());//适合使用于Runnable
    Service.execute(new NumberThread1());//适合使用于Runnable
    //3.关闭线程池
    Service.shutdown();
    //Service.submit();//适合使用于Callable

}

}


优点:提高响应速度(减少了创建线程的时间)、降低资源的消耗(重复利用线程池线程)、**便于线程管理(corePoolSize池大小、maximumPoolSize最大线程数、keepAliveTime线程空置最大时长)**

### 创建多线程有四种方式

1.继承Thread类

2.实现Runnable接口

3.实现Callable接口

4.使用线程池
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值