多线程(一)

1、进程和线程
线程只是一个静态的概念,机器上的一个.class 文件,机器上的一个exe文件,这个叫做一个进程,程序的执行过程:首先把程序的代码放到内存的代码区里面,代码放到代码区后并没有马上开始执行,但这时候说明了一个进程准备开始,进程已经产生,但是还没有开始执行,这就是进程,所以进程其实是一个静态的概念,它本身就不能动,平常所说的进程的执行指的是进程里面主线程开始执行了,也就是main方法开始执行了,进程是一个静态的概念,在我们的机器中实际上运行的都是线程
2、在同一个时间点上,一个cpu只能支持一个线程在执行,因为cpu运行的速度很快,因此我们看起来的感觉就像是多线程一样,
但是如果机器是双cpu或者是多核的,就是真的多线程
3、线程的创建和启动
第一种、定义线程类实现Runnable接口
Thread mythread = new Thread(target);//target 为Runnable几口类型
Runnable 中只有一个方法: public void run();
使用Runnable 几口可以为多个线程提供共享的数据
在实现Runnable接口的类的run方法定义中可以使用Thread 的静态方法
public static Thread currentThread() 获取当前线程的引用
第二种:可定义一个Thread 的子类并重写其run方法,然后生成该类的对象
4、实例演示:
范例一:实现Runnable接口
注意事项:如果一个类实现了Runnable接口,如果想要使用必须在main方法中,在@Test注解的方法内部无法使用

  这种方法是不行的
    @Test
    public void test2(){
        Student student = new Student();
        Thread thread = new Thread(student);
        thread.start();
    }

必须使用下面的方法

    public static void main(String[] args) {
        //这里new了一个线程类的一个对象
        Student student = new Student();
        //这个是方法的调用,只有这个方法执行了才会执行主线程,所以说是不对的
//        student.run();
//        System.out.println("分割线------------------------------");
        //要启动一个线程,就要new一个Thread对象出来
        Thread t = new Thread(student);
        t.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行了=========》" + i);
        }

    }

Student类

public class Student implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("小了白了兔,白了兔了白");
        }
    }
}

范例二:继承Thread类
Teacher 类继承 Thread类并重写了run方法

通过实例化Teacher类的一个对象就可以开辟一个新的线程
public class Teacher extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("小白兔,白又白");
        }

    }
}

测试,这次也不可以在@Test注解的方法内部使用

    public static void main(String[] args){
        Teacher teacher = new Teacher();
        teacher.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("我是百宝的小行家");
        }

    }

使用实现Runnable接口和继承Thread类这两种开辟新城的方法的选择应该优先选择实现Runnable接口这种方法去开辟一个新的线程,因为接口的实现可以实现多个,而类的继承只能是单继承,因此在开辟新线程时能够使用Runnable接口就尽量不要使用从Thread类继承的方式来开辟新的线程

5、线程状态转换
在这里插入图片描述
在这里插入图片描述

(1)sleep方法
注意是:在哪个线程里面调用sleep方法,就是哪个线程睡眠,不是谁调用的谁睡眠

    public static void main(String[] args){
        Teacher teacher = new Teacher();
        teacher.run();
        try {
            Teacher.sleep(10000);
            //在方法中调用另一个类的静态方法的时候,需要使用 类名.方法名  这种方法来调用
            //这里调用睡眠的方法是让主线程睡眠10秒钟
            //在哪个线程里面调用sleep方法,就让哪个线程睡眠,所以现在是主线程睡眠
            for (int i = 0; i < 10; i++) {
                System.out.println("我是百宝的小行家");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

(2)interrupt 打断线程执行

public class Teacher extends Thread{


    @Override
    public void run() {
    //定义一个无限循环,如果不被打断,这个线程就会一直执行
       while(true){
           try {
               sleep(1000);
               System.out.println("我还在快乐的执行。。。。。。。");
           } catch (InterruptedException e) {
               System.out.println("溜了溜了");
               e.printStackTrace();
               return;
           }

       }

    }
}

打断线程


    public static void main(String[] args) {
        Teacher teacher = new Teacher();

        teacher.start();

        try {
            for (int i = 0; i < 100; i++) {
                Teacher.sleep(500);
                System.out.println("主线程也在执行....");
                if(i==3){
                //当执行这句话的时候,Teacher对象的这个线程就会立即被打断,然后抛出异常,执行catch语句的return
                    teacher.interrupt();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

执行结果

D:\jdk\Java\jdk1.8.0_131\bin\java.exe "-javaagent:D:\idea\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar=55524:D:\idea\IntelliJ IDEA 2018.2.4\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk\Java\jdk1.8.0_131\jre\lib\charsets.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\deploy.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\javaws.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\jce.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\jfr.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\jsse.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\management-agent.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\plugin.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\resources.jar;D:\jdk\Java\jdk1.8.0_131\jre\lib\rt.jar;D:\idea\idea-workspace\java_delete\out\production\java_delete;C:\Users\Administrator\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\Administrator\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar com.qf.xc.XCTest
主线程也在执行....
我还在快乐的执行。。。。。。。
主线程也在执行....
主线程也在执行....
我还在快乐的执行。。。。。。。
主线程也在执行....
溜了溜了
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.qf.xc.Teacher.run(Teacher.java:15)
主线程也在执行....
主线程也在执行....
主线程也在执行....
主线程也在执行....
主线程也在执行....
主线程也在执行....
主线程也在执行....
。。。。。。。

这样直接打断会直接报错,其实可以采用比较温和的方式,比如,改变循环的条件

public class Teacher extends Thread{
	这里定义一个boolean变量
    boolean flag = true;
    @Override
    public void run() {

	//以flag为循环的条件
       while(flag){

           try {
               sleep(1000);
               System.out.println("我还在快乐的执行。。。。。。。");
           } catch (InterruptedException e) {
               System.out.println("溜了溜了");
               e.printStackTrace();
               return;
           }

       }

    }
}

当要打断线程的时候

        try {
            for (int i = 0; i < 100; i++) {
                Teacher.sleep(500);
                System.out.println("主线程也在执行....");
                if(i==3){
                //直接改变flag = false,这样就可以了,也不会报错
                    teacher.flag = false;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

(3)join方法
调用join()方法合并线程,将子线程mythread合并到主线程里面

public class People extends Thread {
//这里使用构造方法,可以往里面传一个值,这个值就是该线程的名字
    public People(String s){
        super(s);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
        //这里使用getName()方法获取线程的名字
            System.out.println(getName()+"是第"+i+"个人");
            try {
                Teacher.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }
}

测试类

    public static void main(String[] args) {

        People people = new People("张三");
        people.start();
        try {
        //使用join方法将该线程加入到主线程,也就意味着只有当该线程全部执行完毕时,其他线程才会执行
            people.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行了=======》" + i);
        }

    }

(4)yield()方法

public class People extends Thread {

    public People(String s){
        super(s);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+"是第"+i+"个人");
            try {
                if(i%2==0){
                //当执行到i能被2整除时,当前执行的线程就会让出来让另一个在执行run()方法的线程来优先执行
                    yield();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }
}

(5)线程的优先级

    public static void main(String[] args) {

        People people = new People("张三");
        Student student = new Student();
        //设置people线程的优先级为9,设置student线程的优先级为1
        people.setPriority(Thread.NORM_PRIORITY+4);
        student.setPriority(Thread.MIN_PRIORITY);
        people.start();
        student.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行了=======》" + i);
        }

    }

在这里插入图片描述

6、死锁

public class Student implements Runnable{
    int flag = 1;
    设置两个对象,分别当做两把锁
    Object o1 = new Object();
    Object o2 = new Object();


    @Override
    public void run() {
        System.out.println( Thread.currentThread().getName() + "线程开始执行了=========》");
        if(flag == 1){
            flag = 2;
            synchronized (o1){//在这里锁住了对象o1
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"在这里交集的等待着");
                synchronized(o2){ //只要在这里锁住o2,就可以执行下面的内容
                    System.out.println("输出了,啦啦啦");
                }
            }
        }


        if(flag == 2){
            synchronized (o2){//在这里锁住了o2
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"在这里交集的等待着");
                synchronized (o1){//这里只要锁住了o2 就可以执行下面的
                    System.out.println("我也成功渡劫了");
                }
            }
        }

    }
}

死锁的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因相互申请被其它进程所占用不会释放的资源而处于的一种永久等待的状态。
死锁的四个必要条件:
1、互斥条件:资源不能被共享,只能由一个进程使用
2、请求和保持条件:已经得到资源的进程可以再次申请新的资源
3、非剥夺条件:已经分配的资源不能从相应的进程中被强制地剥夺
4、循环等待条件:系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源

死锁的另一种:递归死锁
所谓递归死锁就是自调用函数,在函数体内直接或间接的调用自己,奇函数的嵌套是函数本身
递归的方式有两种:直接递归和简介递归,直接递归就是在函数中出现调用函数本身,简介递归指函数中调用了其他函数,而其他函数又调用了本函数

避免死锁:
1、加锁顺序
2、加锁时限
在尝试获取锁的时候加一个超时时间,也就意味着在尝试获取锁的过程中,若超过了这个时限则放弃对该锁请求,若一个线程没有在给定的时限内获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试
3、死锁检测
死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按续加锁并且锁超时也不可行的场景
每当一个线程获得锁,会在线程和锁相关的数据结构中将其记下,除此之外,每当有线程清求锁,也需要记录在这个数据结构中,当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁的发生,例如,线程A请求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁。如果线程B确实有这样的请求,那么就是发生了死锁(线程A拥有锁1,请求锁7;线程B拥有锁7,请求锁1)。
当然,死锁一般要比两个线程互相持有对方的锁这种情况要复杂的多。线程A等待线程B,线程B等待线程C,线程C等待线程D,线程D又在等待线程A。线程A为了检测死锁,它需要递进地检测所有被B请求的锁。从线程B所请求的锁开始,线程A找到了线程C,然后又找到了线程D,发现线程D请求的锁被线程A自己持有着。这是它就知道发生了死锁。
那么当检测出死锁时,这些线程该做些什么呢?

一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁(编者注:原因同超时类似,不能从根本上减轻竞争)。

一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。

**** 银行家算法

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值