java多线程编程

参考书籍:Java多线程编程核心技术(高洪岩)

一、java多线程技能

  1.线程的启动

    • 进程:QQ.exe
    • 线程:QQ发一条消息由一个线程处理,QQ传输文件由一个线程处理
    • 创建线程:使用多线程的时候,代码运行结果与代码执行顺序和调用顺序是无关的。使用多线程的时候可能上一行的代码还没执行完,就执行了下一行代码。
      • 继承Thread类
 1 class A extends Thread {
 2     public void run(){
 3         //...
 4     }
 5 }
 6 public class Demo {
 7     public static void main(String[] args) {
 8         A a = new A();
 9         a.start();
10     }
11 }
      • 实现Runnable接口
 1 class A implements Runnable{
 2     public void run() {
 3         //...
 4     }
 5 }
 6 public class Demo {
 7     public static void main(String[] args) {
 8         A a = new A();
 9         Thread t = new Thread(a);
10         t.start();
11     }
12 }
    • 多线程访问同一资源避免非线程安全问题(使用synchronized修饰)
public class Mythread extends Thread {
    
    private int count = 5;
    
    @Override
    synchronized public void run() {
        super.run();
        count--;
                System.out.println("由"+this.currentThread().getName()+"计算,count="+count);
    }
}

public class Test {
    public static void main(String[] args) {
        try {
            Mythread thread = new Mythread();
            Thread a = new Thread(thread,"A");
            Thread b = new Thread(thread,"B");
            Thread c = new Thread(thread,"C");
            Thread d = new Thread(thread,"D");
            Thread e = new Thread(thread,"E");
            a.start();
            b.start();
            c.start();
            d.start();
            e.start();
         } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  2.常用方法

public class Test {
    public static void main(String[] args) {
        try {
            Thread.currentThread();//返回代码段正在被哪个线程调用的信息
            Thread.currentThread().getName();//获取当前线程的名称
            Thread.currentThread().isAlive();//当前线程是否处于活跃状态
            Thread.sleep(1000);//让线程休眠1秒
            Thread.currentThread().getId();//获取当前线程的唯一标识
            Thread.currentThread().interrupt();//停止当前线程(此方法只是打一个停止线程的标记,并非真正停止线程,线程会继续执行)
            Thread.currentThread().interrupted();//测试当前线程是否已经中断,同时清除该中断状态
            Thread.currentThread().isInterrupted();//测试当前线程是否已经中断,只是测试,不做其他处理
            Thread.currentThread().stop();//弃用的方法,直接停止线程,使一些请理性工作无法完成,同时解锁锁定的对象,不能保证数据一致
            Thread.currentThread().suspend();//弃用的方法,暂停线程,使用此方法可能会造成对公共对象的独占,使其他线程无法访问公共同步对象
            Thread.currentThread().resume();//弃用的方法,继续暂停的线程
            Thread.currentThread().setPriority(10);//设置优先级,1 <= x <= 10;
            Thread.currentThread().yield();//放弃当前cpu资源,让给其他任务去独占cpu
         } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  3.守护线程

守护线程是一种特殊的线程,当进程中不存在非守护线程时,守护线程则自动销毁。典型的守护线程是垃圾回收线程。将线程的daemon设置为true,则该线程作为守护线程存在。如下代码,将main中new出的thread线程设置daemon为true,作为main线程的守护线程,下面代码输出结果为

 

每一秒输出一个数字,可能不会输出i=6,也可能i=6在end之后输出。当main线程sleep结束,执行“System.out.println("end");”之后,main线程停止,thread作为守护线程同时停止。

 

 

public class Mythread extends Thread {
	
	private int i = 0;
	@Override
	public void run() {
		try {
			while (true){
				i++;
				System.out.println("i= " + i);
				Thread.sleep(1000);
			}
		} catch (InterruptedException e){
			e.printStackTrace();
		}
	}
}
public class Test {
	public static void main(String[] args) {
		try {
			Mythread thread = new Mythread();
			thread.setDaemon(true);
			thread.start();
			Thread.sleep(5000);
			System.out.println("end");
 		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 二、对象及变量的并发访问

  1.synchronized同步方法

    • 方法内定义的变量线程安全,实例变量(C中的全局变量)非线程安全
    • synchronized关键字可以用在方法上加在方法之前。对方法内的对象加锁,从而实现同步。
    • 多个对象多个锁:多个线程分别访问不同的对象时,会创建多个锁,线程安全(没有共享变量因此不存在非线程安全问题),但不是同步执行。
    • 脏读:读取变量时,此值被其他线程修改过。synchronized可以解决脏读的问题。
    • synchronized锁重入:当一个线程获得一个对象锁后,再次请求此对象锁是可以再次得到该对象锁的。
    • 出现异常的时候锁会自动释放。
    • 同步不具有继承性:父类方法用synchronized修饰,但是子类方法没有用synchronized修饰,则子类非线程安全。

  2.synchronized同步代码块

    • synchronized方法的弊端:同步方法运行速度慢,效率低下,可以用同步代码块实现的尽量用同步代码块实现。
    • 同步代码块的使用:synchronized(this){},同步代码块是锁定当前对象的。
    • 同步代码块实现的效果是一半同步一半异步的。
    • 将任意对象作为对象监视器:synchronized(Object obj){}锁定obj对象。
    • synchronized的三个结论
      • 当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。
      • 当其他线程执行x对象中synchronized同步方法时呈同步效果。
      • 当其他线程执行x对象方法里面的synchronized(this){}代码块时呈同步效果。
    • 静态同步synchronized方法和synchronized(class)代码块:锁class类。验证不是一个锁的方式:一个类中两个static用synchronized修饰,一个非static的用synchronized修饰方法,分别启动线程调用这三个方法。
    • 数据类型String的常量池特性(String常量池可能导致某个线程无法启动,因此在同步代码块中一般不使用String作为锁对象,而是使用其他)
    • 同步synchronized方法无限等待与解决:使用同步代码块解决。
    • 多线程死锁:监测是否有死锁发生:1>cmd  2>进入jdk安装文件bin目录(cd C:/Users/administrator/AppData/...../com.sun.java.jdk.win32.x86_1.6.0.013/bin)  3>执行jsp命令找到运行的线程run标志的id  4>执行jstack命令(jstck -l 3244)
    • 内置类与静态内置类
    • 锁对象的改变:只要对象不变,即使对象的属性变了,运行结果还是同步的。注意String类型,String使用的是引用,修饰值的时候可能会改变对象。

  3.volatile关键字

    • 在启动线程后,变量的值是存在与公共内存和线程的私有内存中的,volatile是保证当前线程强制性的从公共内存中取值。所以volatile一般用于修饰只读的变量,对于修改的变量并不能保证线程的安全。
    • synchronized和volatile的比较
      • volatile是线程同步的轻量级实现,性能比synchronized好;volatile只能修饰变量,synchronized能修饰方法,代码块
      • 多线程访问volatile不会发生阻塞,synchronized会出现阻塞
      • volatile只能保证数据的可见性,不能保证原子性,synchronized会将私有内存和公共内存做数据同步,可以保证原子性和可见性。
    • 原子类 java.util.concurrent.atomic.*;该包下的对象操作是原子操作(从内存中取值,修改值,放入到内存。原子操作,线程安全)。但是当在一个非synchronized修饰的方法中对原子类对象进行多个操作时,也是非线程安全的。(每个操作是线程安全,多个操作之间非线程安全)

三、线程间的通信

  1.等待/通知机制

    • wait()和notify();wait和notify方法只能在同步方法或者同步方法块中执行;wait方法作用是使当前线程进行等待,释放对象锁,wait之后的代码不再执行;notify由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出notify通知,并使它等待获取该对象的对象锁。执行notify之后,待将nofity下面的代码执行完成(退出synchronized)之后,释放对象锁。【wait使线程停止运行,notify使停止的线程继续运行】
    • 当interrupt()方法遇到wait()方法:对wait状态的线程执行interrupt()方法,抛出InterruptedException
    • notify和notifyAll:notify唤醒一个线程,notifyAll唤醒所有的线程
    • wait(long):在long的时间内如果有其他线程唤醒该线程,该线程被唤醒,否则在long时间后,自动唤醒。
    • 生产者/消费者模式实现:一个线程赋值,另一个线程取值。1>多个生产者和多个消费者造成的假死问题2>notify解决假死问题3>操作栈的实现
    • 通过管道进行线程间通信:字节流PipedInputStream和PipedOutputStream字符流:PipedReader和PepedWriter;核心:connect的使用(inputStream.connect(outputStream)和outputStream.connect(inputStream))

  2.方法join的使用

    • join和join(long):join主线程调用join方法,在子线程执行完成后,主线程继续执行。join(long)主线程在long时间内等待子线程执行,期间内,子线程返回主线程立马结束等待状态,long时间后子线程没有返回,主线程不再等待,继续执行。
    • join和interrupt方法彼此遇到,会出现异常
    • join(long)和sleep(long)的区别:join(long)内部由wait(long)实现。所以join(long)在进入等待状态之后会释放对象锁,而sleep(long)不会释放对象锁

  3.类ThreadLocal的使用

    • 使每个线程有自己的公有变量,用于存储线程的私有数据。set()和get()

  4.InheritableThreadLoca的使用

    • InheritableThreadLocal:在子线程中获得父线程中继承下来的值。
    • 值继承再修改

 

 

 

 

 

 

 

 

 

 

                   

posted @ 2017-07-14 18:11  dream_ich  阅读(143)  评论(0)    收藏  举报