多线程技术

线程同步

  • 由于同一个进程的多个线程有时会共享一个存储空间(一个对象),在他们同时访问一个对象时,就会发生冲突问题 ;例如:如果某一线程在更新该对象的同时,而另外一个线程也试图去更新或读取该对象,这样就会破坏数据的一致性,为避免多个线程同时访问一个共享对象带来的访问冲突问题,所以提供了线程同步方法同步、对象同步机制;

              方法同步是为了防止多线程访问同一方法导致数据奔溃,而在定义方法时加关键字  synchronized  修饰可以保证该线程在其他任何线程访问之前完成执行;

public   synchronized  void  methodName([ parameterList ]){

//对共享对象的操作

}

  • 线程同步可以有效地控制多个线程同时访问一个对象的问题,从而有效的避免一个线程刚生成的数据就被其他的线程生成的数据覆盖等问题,线程同步其实就是一种等待机制,为了保证证数据在方法中被访问时的正确性 , 在访问时加入 锁机制 synchronized , 当一个线程获得对象的访问锁 , 独占资源 , 其他线程必须等待 , 使用后释放锁即可 . 存在以下问题 :

             1.一个线程持有锁会导致其他所有需要此锁的线程挂起 ;

             2. 在多线程竞争下 , 加锁 , 释放锁会导致比较多的上下文切换 和 调度延时,引 起性能问题 ;

             3. 如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒 置 , 引起性能问题 ;

同步方法:

  • 由于可以通过  private   关键字保证数据对象只能被方法访问,而在线程中提供了    synchronized  关键字,其中包括了     synchronized   方法和   synchronized    块;

同步方法 : public synchronized void method(int args) {}

  • synchronized  方法使用锁机制,每次都必需调用该方法的对象的锁才可以执行,否则就会发生阻塞,方法一旦执行就会独占该锁,直到该方法结束释放锁,后面被阻塞的线程才能够继续执行;
  • 使用同步方法时如果将一个大的方法声明为    synchronized    方法将会影响效率,
  • 同步方法中只有需要被修改的内容才需要锁,但是锁使用的太多就会造成资源的浪费;

同步块:

synchronized (Obj ) { }

Obj -----同步监视器他可以是任意的对象,一般使用共享资源作为同步监视器;

在同步方法中无需指定同步监视器,因为方法的监视器就是  this  (对象本身),或者是class;

同步监视器的执行过程:

  • 第一个线程访问,锁定同步监视器,执行其中的代码;
  • 第二个线程访问,发现同步监视器被锁定,无法进行访问;
  • 第一个线程访问完成,解锁同步监视器;
  • 第二个线程访问,发现同步监视器没有锁,然后锁定进行访问;

线程通信

线程通信是建立在生产者与消费者的模型上的;即一个线程产生输出(相当于生产产品,该线程称为生产者,产生一串数据流),另一个线程进行输入(相当于消费者,该线程称为消费者,消耗数据流中的数据),先有生产者生产,才能有消费者消费。生产者没有生产之前通知消费者等待。而生产后通知消费者消费,消费者消费之后再通知生产者生产。这就是等待通知机制(wait/Notify);

实例:

package com.zhang.demo;

public class WaitNotify {
    public static void main(String[] args) {
        ProducerThread pt  = new ProducerThread();
        System.out.println("生产者为:sum="+pt.getSum());
    }
}
class ProducerThread extends Thread{
long sum=0;
ProducerThread(){
    start();
}
public void run() {
    synchronized (this) {
        for (int i = 0; i < 500; i++)
            sum += i;

            System.out.println("生产者生产完毕数据:sum=" + sum);
            notify();
        }
    }

    //run

    synchronized public long getSum () {
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return sum;
    }
}

以上代码编译运行的结果如下:

生产者生产完毕数据:sum=124750
生产者为:sum=124750

        一般而言,当线程获得某个对象的锁之后,若该线程调用   wait()  方法,则会退出所占用的处理器,并打开该对象的锁,转为阻塞状态,并允许其他的同步语句获取该对象的锁。当执行条件满足之后,将调用    notify()方法,唤醒处于阻塞状态的这个线程,并转换为可运行状态,将有机会获得该对象的锁。但是如果一个线程调用 wait()方法 后进入该共享对象的等待状态,应确保有一个独立的线程最终将会调用  notify()方法,以使等待共享对象的线程回到可运行的状态;

死锁

当有多个线程各自占有一些资源,并且互相等待其他的线程占有的资源释放才可以运行,而导致多个线程都在等待资源,形成停止执行的形式,某一同步块同时拥有  ”两个以上的对象的锁“  时就有可能导致产生   死锁   问题;(就是线程之间因相互等待对方的资源,而不能继续运行的状态)

死锁的避免方法:

产生死锁实际就是资源不够用,只要破坏产生死锁的条件中的一个就会避免产生死锁;

产生死锁的四个必要条件

  1. 互斥事件:一个资源每次只能被一个进程使用;
  2. 请求与保持条件:一个进程由于请求资源而阻塞时,对已获得资源保持不进行释放;
  3. 不剥夺条件:进程已获得资源,在未使用完成之前不能剥夺;
  4. 循环等待条件:多个进程之间形成一种头尾相接的循环等待资源关系;

线程组

线程组(Thread Group)就是包含了多个线程的对象集,并可以拥有一个名字或者相关的属性,用于统一管理组中的线程。

java语言提供了一个线程组类 ThreadGroup,可用于对线程组中的线程和线程组进行操作,如启动或阻塞组中的线程,该类的构造方法为  ThreadGroup( String GroupName)。

在创建线程之前可以创建一个 ThreadGroup 对象,在将创建的线程加入,如下:

ThreadGroup tg  = new ThreadGroup("MyThreadGroup");
        Thread writer = new Thread(tg, "writer");
        Thread reader = new Thread(tg, "reader");
        writer.start();
        reader.start();
  1. getName ():返回线程组的名字;
  2. getParent (): 返回该线程的父线程的名字;
  3. activeCount ():返回线程组中当前激活的线程数目,包括子线程中的活动线程;
  4. interrupt () / resume () / stop () / suspend():向线程组及其子组中的线程发送一个中断 / 唤醒 /停止 / 挂起信号;

你可能感兴趣的:(java,多线程)