Java SE学习总结 Day (25)

Day 25开篇:
      
       "今天java基础主要学习了多线程的死锁问题,同步资源线程安全解决同步代码块同步方法线程加入,线程礼让,线程守护,线程优先级设置,线程终止,Runnable接口等"



知识点反馈:

今天的知识点总结的思维导图

一.死锁

1.Java的同步机制,虽然可以解决线程安全的问题,但是也有比较严重的弊端:

(1)效率极低

(2)使用不当可能造成死锁

2.死锁如何解决?

没法解决(当今世界上根本没有任何办法解决的一个问题)

死锁是一个没有解决方案的问题,只能说尽量避免(破坏死锁的任意一个必备条件即可),千万不要因为好奇心去写出一个死锁代码出来,因为很危险

3.死锁的产生:

(1)互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放

(2)请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。

(3)不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用

(4)循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

 

二.同步资源

1.我们一般使用synchronized的时候,他有个弊端:

 (1)需要同步锁资源

 (2)他的代码结构不健壮

 

 2.JDK5的的版本提供一个新的锁对象:Lock

  (1)void lock(): 获取锁。

  (2)void unlock(): 获取锁。

例子:

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

public class BankThread01 extends Thread {

 

    BankThread01(String name){

        super(name );

    }

    static int count = 5000;

    //锁对象

    Lock lock = new ReentrantLock();

 

    @Override

    public void run() {

        while (true){

            //锁开始

            lock.lock();

            if(count>0){

                count-=100;

                System.out.println(Thread.currentThread().getName()+"取出100元钱,还剩余:"+count);

 

            }else{

                System.out.println("不好意思无余额");

                break;

            }

            //锁结束(释放锁)

            lock.unlock();

        }

    }

}

 

三.线程安全解决

1.线程安全的问题解决:

(1)同时存在两个以上的线程可能造成线程安全

(2)多个线程共享一个资源,可能造成线程安全

 

2.根据原因解决问题:

 两个线程,这个是不能动的,因为需求就是如此,所有只能共享的地方入手。

 

3.同步代码块:

        synchronized (同步监视器){

            需要被同步的代码

        }

(1)同步监视器是一个什么东西?

就是一个对象(锁对象),任何的对象都存在一把锁,而且只有一把钥匙

也就是,这个地方的同步监视器,你只需要放一把锁就oK,任何锁都行。

new Object()行不行?

行,没有问题,但是每个线程是自己锁自己的。

这个锁对象,应该是所有线程都共享的一把锁。

(2)注意的事项:

 <1>锁对象可以是任意对象

<2>锁对象必须是多个线程共享的对象

 <3>如果调用sleep方法的线程,不会释放锁资源。

 <4>如果你的代码不存在线程安全的问题,不要去使用同步块或者函数,因为效率特别低

 

4.同步方法:

 (1)synchronized修饰的方法被称之为同步方法

 (2)注意的事项:

  <1>非静态的方法锁对象其实this对象,谁调用我 我代表哪个对象(不是持有同一把锁)

  <2>静态方法锁的对象其实的class的文件对象(静态资源对象,持有的是同一把锁)

  <3>一般,代码量比较少的 请尽量使用同步代码块,不要使用同步方法,一般来说,方法应该是一个灵活扩展的代码,不要使用任何形式去限制它

 

四.线程

1.线程加入:

 (1)public final void join():等待该线程终止

 (2)被join调出的线程,必须等本线程执行完毕之后,其他线程才能允许参与抢夺

    注意调用和启动的位置。

 

 例子:

public class Demo {

    public static void main(String[] args) {

        MyThread m1 = new MyThread();

        MyThread m2 = new MyThread();

        MyThread m3 = new MyThread();

 

        m1.setName("小花");//二媳妇

        m2.setName("小翠");//大媳妇

        m3.setName("小李");//3000万

 

        m3.start();

        try {

            //等待m3终止后,其他线程才能参与抢夺

            m3.join();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

 

        m1.start();

        m2.start();

 

    }

}

 

2.线程礼让:

(1)public static void yield():线程礼让(先暂时停止当前正在执行的线程对象,并执行其他线程对象)

(2)线程礼让,并不能保证多个线程一定是执行一次就放出执行权,只能尽可能保证多个线程的执行更加的和谐。

例子:

public class Demo {

    public static void main(String[] args) {

        MyThread m1 = new MyThread();

        MyThread m2 = new MyThread();

        MyThread m3 = new MyThread();

 

        m1.setName("小花");

        m3.setName("小李");

        m1.start();

        m3.start();

 

    }

}

3.线程守护:

(1)public final void setDaemon(boolean on):线程守护(被标记为守护线程的线程,当被守护线程死亡后,守护线程也死亡)

例子:

public class Demo {

    public static void main(String[] args) {

        MyThread m1 = new MyThread();

        MyThread m2 = new MyThread();

        MyThread m3 = new MyThread();

 

        m1.setName("小花");//二媳妇

        m2.setName("小翠");//大媳妇

        m3.setName("小李");//3000万

 

        //当小李死亡的时候,小花和小翠也不想活了

        m1.setDaemon(true);

        m2.setDaemon(true);

 

        m1.start();

        m2.start();

        m3.start();

 

    }

}

 

4.线程休眠:

(1)public static void sleep(long millis):线程休眠

(2)所谓的线程休眠,其实就是当前现在执行到某一个片段的时候进行睡眠,并且让出时间片段(执行权)

(3)其他线程就会拿到执行权。(如果多个线程休眠的时间是一致的时候,每个线程的执行片段就会相对和谐)

例子:

public class Demo {

    public static void main(String[] args) {

        MyThread m1 = new MyThread();

        MyThread m2 = new MyThread();

 

        m1.setName("小花");

        m2.setName("小翠");

 

        m1.start();

        m2.start();

    }

}

 

5.线程优先级设置:

 (1)假如我们的计算机只有一 CPU,那么CPU在某一个时刻只能执行一条指令,

    线程只有得到CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢?

(2)线程有两种调度模型:

 <1>分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片

<2>抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。

 (3)Java使用的是抢占式调度模型。

 演示如何设置和获取线程优先级

public final int getPriority():获取当前线程的优先级【默认优先级为5】

 mian方法的优先级仍然5,那么为啥经常优先其他线程运行呢?

 谁先开启的问题,一般来说 先运行的线程具备先天的优势,有几率可能优先运行

 public final void setPriority(int newPriority)

 优先级的范围从1-10,1的优先级为最高,10的优先级为最低

 如果线程的优先级超如如上范围,则产生程序异常:IllegalArgumentException

 线程的优先级设置,并不能准确的提高线程的运行的优先程度,只能是尽可能的让线程优先级高的线程先运行(还是存在很大的随机性)

例子:

public class Demo {

    public static void main(String[] args) {

        MyThread m1 = new MyThread();

        MyThread m2 = new MyThread();

        m1.setName("小花");

        m2.setName("李老师");

        m2.setPriority(1);//

        m1.start();

        m2.start();

 

        System.out.println(m1.getPriority());

        System.out.println(m2.getPriority());

        System.out.println("-----"+Thread.currentThread().getPriority());//mian方法的优先级仍然5,那么为啥经常优先其他线程运行呢?

 

    }

}

6.线程终止:

(1)void stop():线程终止

     不推荐使用:某一个线程的终止不应该造成整个JVM被关掉(stop一旦运行会终止掉整个虚拟机不合理)

(2)void interrupt():线程中断

      推荐使用:某一个线程产生意外,只是中断此次线程的运行,并不会干扰到其他线程的运行,并且抛出一个InterruptedException异常(这个异常不会造成JVM终止,只是给一个提示警告的作用)

例子:

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

 

public class Demo {

    public static void main(String[] args) throws FileNotFoundException {

        MyThread m1 = new MyThread();

        MyThread m2 = new MyThread();

 

        m1.setName("小花");

        m2.setName("李老师");

 

        m1.start();

        m2.start();

 

    }

}

 

7.Runnable接口:

(1)创建线程的两种方式:

       <1>将线程类作为Thread的具体子类,重写run方法(不强制)

       < 2>将线程类作为Runnable的接口实现类,重写run(强制)

            1)自己定义一个线程类,实现Runnable

            2)在线程类里面重写run方法

            3)创建一个Runnable子实现类对象

            4)创建一个Thread的具体类,并且将Runnable具体实现类对象传入

            5)调用Thread对象的start方法,由他来调用MyRunnable的run方法

 

    (2)两种方式,我们一般使用哪一种?

        一般我们优先选择Runnable。

        1)因为使用Runnable可以打破继承的局限性,因为对于线程类来说,不一定只是实现run方法,有可能还要继承其他类,那么Thread就无法实现了。

        2)代码更加合理,因为一个类如果要实现线程就必须重写run方法,而接口更加合理的强制了我们必须重写

        3)当多个线程需要共享线程对象的某一个数据的时候,Runnable不需要使用静态修饰,因为Runnable的实现类只有一个,而所有Thread都是共享一个Runnable实现类对象

 

例子:

public class Demo{

    public static void main(String[] args) {

        //创建Runnable实现类对象

        MyRunnable mr = new MyRunnable();

        //创建一个Thread的具体类,并且将Runnable具体实现类对象传入

        //直接对象构造设置线程姓名

        Thread t = new Thread(mr,"小明");

        //t.setName("小明");

        t.start();

 

        Thread t2 = new Thread(mr,"小花");

        //t2.setName("小花");

        t2.start();

    }

}

 

 

 

 

 

 

发布了35 篇原创文章 · 获赞 7 · 访问量 3896

猜你喜欢

转载自blog.csdn.net/weixin_45406656/article/details/104350895
今日推荐