Java 线程

198 阅读6分钟

一. 理解程序、进程、线程的概念

1. 程序可以理解为静态的代码
2. 进程可以理解为执行中的程序
3. 线程可以理解为进程的进一步细分, 程序的一条执行路径

二. 如何创建java程序的线程

方式一: 继承于Thread类

   ① class ImplThread extends Thread {
        @Override
        public void run() {
//            子线程执行的代码
            super.run();
        }
    }
    
   ② new Thread(){
            @Override
            public void run() {

                super.run();
            }
        }.start();

方式二: 实现Runnable接口的方式

  ① public static void main(String[] args) {
        ImplRunnable runn = new ImplRunnable();
        Thread thread = new Thread(runn);
        Thread thread1 = new Thread(runn);
    }

    class ImplRunnable implements Runnable {
        @Override
        public void run() {
//            子线程执行的代码
        }
    }
    
  ② new  Thread(new Runnable() {
            @Override
            public void run() {
                
            }
        }).start();

对比 继承方式和实现方式的区别

1. 联系: public class Thread implements Runnable
2. 实现的方式优于继承的方式
	①. 避免了java单继承的局限性
	②. 如果多个线程要操作同一份资源(或数据), 更适合使用实现的方式

三. Thread的常用方法:

1. start(): 启动相乘并执行相应的run()方法
2. run(): 子线程要执行的代码放入run()方法中
3. currentThread(): 静态的, 调取当前的线程
4. getName(): 获取此线程的名字
5. setName(): 设置此线程的名字
6. yield(): 调用此方法的线程释放当前CPU的执行权
7. join: 在A线程中调用B线程join()方法, 表示: 当执行到此方法, A线程停止执行, 直至B线程执行完毕, A线程再接着join()之后的代码执行
8. isAlive(): 判断当前线程是否还存活9
9. sleep(long l): 线程休眠(单位毫秒)
10. 线程通讯: wait() notify() notifyAll()

四. 线程的优先级

getPriority(): 返回线程优先值
setPriority(int newPriority): 改变线程的优先级

线程优先级越高, 不代表一定会最先执行. 而是抢占时间片的概率增大

五. 线程的生命周期(五种状态)

	① 新建: 当一个Thread类或其子类的对象被声明并创建时, 新生的线程对象处于新建状态
	② 就绪: 处于新建状态的线程被start()后, 将进入线程队列等待CPU时间片, 此时它已具备了运行的条件
	③ 运行: 当就绪的线程被调度并获得处理器资源时, 便进入运行状态, run()方法定义了线程的操作和功能
	④ 阻塞: 在某种特殊情况下, 被认为挂起或执行输入输出操作时, 让出CPU并临时终止自己的执行, 进入阻塞状态
	⑤ 死亡: 线程完成了它的全部工作或线程被提前强制性的终止

线程的补充

java中线程分为两类: 一种是守护线程, 一种是用户线程
	①. 它们几乎每个方面都是相同的, 唯一的区别是判断JVM何时离开.
	②. 守护线程是用来服务用户线程的, 通过再start()方法前调用thread.setDaemon(true)可以把一个用户线程编程一个守护线程
	③. java垃圾回收就是一个典型的守护线程
	④. 若JVM中都是守护线程, 当前JVM将退出

六. 使用多线程的优点

背景: 只使用单个线程完成多个任务(调用多个方法), 肯定比用多个线程类完成用的时间更短, 为何仍需多线程呢?

1. 提高应用程序的响应. 对图形化界面更有意义, 可增强用户体验
2. 提高计算机系统CPU的利用率
3. 改善程序结构. 将即长又复杂的进程分为多个线程, 独立运行, 利于理解和修改

七. 线程安全问题

  1. 线程安全问题存在的原因?
	由于一个线程在操作共享数据过程中, 未执行完毕的情况下, 另外的线程参与进来, 导致共享数据存在了安全问题
  1. 如何解决线程的安全问题
	必须让一个线程操作共享数据完毕以后, 其他线程才有机会参加与共享数据的操作
  1. java如何实现线程的安全: 线程的同步机制
	方式一: 同步代码块
		synchronized(同步监视器){
    			// 需要被同步的代码块(即为操作共享数据的代码)
        	}
        
        1. 共享数据: 多个线程共同操作的同一个数据(变量)
        2. 同步监视器: 由一个类的对象来充当. 那个线程获取此监视器, 谁就执行{}里被同步的代码. 俗称:锁
	要求: 所有的线程必须共用同一把锁
	注: 在实现的方式中, 考虑同步的话, 可以使用this来充当锁. 但是在继承的方式中, 慎用this
    
	方式二: 同步方法
		将操作共享数据的方法声明为synchronized. 即此方法为同步方法, 能够 保证当其中一个线程执行此方法时, 其他线程在外等待直至此线程执行完此方法

同步线程的弊端: 由于同一个时间只能有一个线程访问共享数据, 效率变低了

八. 死锁

  • 死锁: 不同的线程分别占用对方需要的同步资源不放弃, 都在等待对方放弃自己需要的同步资源, 就形成了线程的死锁

  • 死锁的解决方法: ① 专门的算法、原则 ②尽量减少同步资源的定义

死锁代码 一.

    static StringBuffer sBuffer1 = new StringBuffer();
    static StringBuffer sBuffer2 = new StringBuffer();

    public static void main(String[] args) {
        new Thread() {
            @Override
            public void run() {
                synchronized (sBuffer1) {
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    sBuffer1.append("A");
                    synchronized (sBuffer2) {
                        sBuffer2.append("B");
                        System.out.println(sBuffer1.toString());
                        System.out.println(sBuffer2.toString());
                    }
                }
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                synchronized (sBuffer2) {
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    sBuffer1.append("C");
                    synchronized (sBuffer1) {
                        sBuffer2.append("D");
                        System.out.println(sBuffer1.toString());
                        System.out.println(sBuffer2.toString());
                    }
                }
            }
        }.start();
    }

二.

 class A {
        public synchronized void foo(B b) {// 锁: A的对象a
            try {
                Thread.currentThread().sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            b.last();
        }

        public synchronized void last() {// 锁: A的对象a

        }
    }

    class B {
        public synchronized void bar(A a) {// 锁: B的对象b
            try {
                Thread.currentThread().sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            a.last();
        }

        public synchronized void last() {// 锁: B的对象b

        }
    }

    class DeckLock implements Runnable {
        A a = new A();
        B b = new B();

        public void init() {
            a.foo(b);
        }

        @Override
        public void run() {
            b.bar(a);
        }
    }


    public static void main(String[] args) {
        DeckLock deckLock = new Test_Thread().new DeckLock();
        new Thread(deckLock).start();
        deckLock.init();
    }

九. 线程通讯

wait()与notify()、notifyAll()

1. wait(): 令当前线程挂起并放弃CPU、同步资源, 使别的线程可访问并修改共享资源, 而当前线程排队等候再次对资源的访问

2. notify(): 唤醒正在排队等待同步资源的线程中优先级最高者结束等待

3. notifyAll(): 唤醒正在排队等待资源的所有线程结束等待

java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用, 否则会报java.long.IllegalMonitorStateException异常