A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小石姐姐 于 2018-6-8 09:25 编辑

多线程两种方式,建议用第二种方法,

实现方式1:定义一个类,让这个类继承Thread类,并且重写Thread里面的run方法.但是运行时需要调用的不是run方法,是start方法,它会执行run方法里面被重写的方法;

可以通过setName来更改当前线程名字,可以通过getName来获取当前线程名字

cpu执行程序时,在高速切换着线程,而且是随机的切换


实现方式2:为什么推荐使用这种方式呢,这种方式是通过implrments(实现)来实现多线程的,所以可以再implements的同时继承其他的类;也是为什么有两种多线程实现的原因

它的实现流程是创建一个类去implements(实现)Runnable接口;实现run方法,创建这个类的实例,创建一个Thread的对象(构造函数)来接收刚刚创建的类的对象,用Thread对此昂来调用start启动.

这时候自建类实现了Runable,但是不论是Runable还是咱们的自建类都没有对于线程操作的方法,这时候就需要用到Thread的静态方法了.

Thrwad的方法:

方法:static Thread currentThread();返回当前线程对象:举例:Thread t = Thread.currentThread();

然后还注意,可以共享一个Runnable的实现对象(也就是我们自定义的类).

方法:static void sleep(long millis);这个形参是毫秒;让当前线程休眠一会

多线程并发去访问同一个数据时就有可能发生问题,(看视频卖火车票案例);


关键词:synchronized,同步,可以修饰代码块和方法,被修饰的代码块和方法一旦被某个线程访问则直接锁住,其他线程将无法访问


同步代码块的使用:

synchronized(锁对象){


}

注意:锁对象需要被所有的线程共享


同步:安全性高,效率低

非同步:安全性低,效率高


关于同步方法

使用关键字synchronized修饰的方法,一旦被被一个线程访问则整个方法都会被锁死,其他线程无法访问

可以把有问题的代码用一个方法封装然后用synchronized进行修饰

,这就是一个同步方法,同步方法有默认的锁对象

非静态的同步方法默认锁对象是This.

静态的同步方法的默认锁对象是字节码对象


扩展(很重要)

另外一种同步方法:

JDK 5中新增的加锁方式: java.util.concurrent.locks.Lock接口

private Lock lock = new ReentrantLock();    // 创建锁对象, 该锁对象也要所有线程共享唯一一个

lock.lock();  //加锁

try {

        // 需要同步的代码

} finally {

    // 在finally中保证释放锁

    lock.unlock();   //这里是释放锁,用的try{}finally就是为了保证可以在finally中释放锁

}  

****这里要配合try{}finally一起使用

线程放锁3种情况:

1.同步代码块执行完毕, 同步方法执行结束, Lock调用unlock()

2.线程内部代码发生了异常, 导致线程终止

3.线程调用了wait()方法进行等待, 会立刻释放锁注意: Thread.sleep()不会释放锁

常见线程问题: 死锁

死锁 (dead lock): 同步代码块中的线程不出来, 也不释放锁; 同步代码块外的线程拿不到锁, 只能等在外面.

发生死锁的原因:

  • 同步代码块内的线程, 可能处在死循环, IO阻塞, sleep()状态, 导致内部持有锁的线程无法出同步代码块
  • 多个线程互相持有锁又不释放锁: 两个线程执行的任务都是双层同步代码块, 每层同步都需要一个锁, 两个线程中同步代码块的锁是相反的
死锁的结果: 程序卡死, 无法继续执行

如何避免死锁:

  • 避免在同步代码块中执行死循环, IO阻塞操作, sleep()
  • 避免多个线程互相持有锁又不释放锁的情况
代码演示:

/*

死锁的一种情况:

        多个线程互相争夺对方持有的锁

*/

public class Demo {

        // 定义2个锁对象

        public static String lock1 = "{锁1}";

        public static String lock2 = "{锁2}";


        public static void main(String[] args) {

                // 创建2个线程对象

                MyThread1 thread1 = new MyThread1();

                MyThread2 thread2 = new MyThread2();

                thread1.setName("MyThread1");

                thread2.setName("MyThread2");

                // 开始执行

                thread1.start();

                thread2.start();

                // 注意观察控制台的小红点

        }

}


class MyThread1 extends Thread {


        @Override

        public void run() {

                System.out.println(getName() + "线程开始执行run()");

               

                // 外层同步代码块, 使用锁1

                synchronized (Demo.lock1) {

                        System.out.println(getName() + "线程拿到了" + Demo.lock1);

                        

                        // 内层同步代码块, 使用锁2

                        System.out.println(getName() + "线程想要" + Demo.lock2);

                        synchronized (Demo.lock2) {

                                System.out.println("个别情况:" + getName() + "持有2把锁");

                        }

                }

        }


}


class MyThread2 extends Thread {


        @Override

        public void run() {

                System.out.println(getName() + "线程开始执行run()");

               

                // 外层同步代码块, 使用锁2

                synchronized (Demo.lock2) {

                        System.out.println(getName() + "线程拿到了" + Demo.lock2);

                        

                        // 内层同步代码块, 使用锁1

                        System.out.println(getName() + "线程想要" + Demo.lock1);

                        synchronized (Demo.lock1) {

                                System.out.println("个别情况:" + getName() + "持有2把锁");

                        }

                }

        }

}

线程之间的通信

java.lang.Object类

// 成员方法:

        void wait(): 使当前线程处于等待状态, 并且会立刻释放锁

        void notify(): 随机唤醒一个处于等待状态的线程

        void notifyAll(): 唤醒所有处于等待状态的线程

        // 这三个方法必须在同步代码块中用锁对象来调用, 否则会抛IllegalMonitorStateException异常

-----------------------------------------------------

sleep()wait()的区别:

  • sleep


    • 让当前线程在指定时间内睡眠, 时间结束后继续执行
    • 是Thread类的静态方法
    • 不会释放锁
  • wait


    • 让当前线程等待, 直到有人唤醒
    • 是Object类的非静态方法
    • 会立刻释放锁
注意:         * wait的线程必须要有某个线程调用了notify才能唤醒, 如果所有的线程都wait了, 那程序整个就全都在等待了. 所以在写代码时必须考虑仔细


1 个回复

倒序浏览
很实用!!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马