java并行程序基础

1 进程

	1 进程和线程之间的关系:进程是线程的容器。

2 线程


1 线程的执行流程:

  • **线程调用start()**方法时,表示线程开始执行;
  • 当线程执行时,处于RUNNABLE状态,表示线程所需的一切资源都准备好了;
  • 当线程遇到synchronized同步块,就进入BLOCED阻塞状态,这时线程会暂停执行,直到获得请求的锁。

2 WAITING 和 TIMED_WAITING:

  • WAITING 和 TIMED_WAITING都表示等待状态区别是WAITING会进入一个无时间限制的等待,TIMED_WAITING会进行一个有限的等待。
  • WAITING 的线程是在等待一些特殊事件:比如,wait()方法等待notify()方法,join等待目标线程的终止。
  • 线程的基本操作
    1 注意:不要用run()开启新线程,它只会在当前线程中,串行执行run()中的代码。
    2 创建线程的两种方式:实现Runnable接口;继承Thread类;
  //Runnable接口
   public interface Runable{
		public abstract void run();
}

4 终止线程:

  • 为何终止:一般线程会自动终止,但是一些服务端的后台线程会常驻系统,它们本身是无穷循环,用于提供某些服务。
    1 使用stop()? //未来将被废弃;
    stop()有一个局限性: 当写入两个相同的值,当线程写到一半,并强行终止,对象就会被写坏。从而出现
    写读不一致的现象。stop发生的问题很难排查!,除非你很清楚你在干什么,否则不要随便使用stop()去
    终止一个线程
    2 自行决定何时退出
    1 在自己的线程类中增加一个stopMe()方法:
ChangeObjectThread extends Thread{
	volatile Boolean stopme = false;
	public void stopMe(){
		stopme = true;
	}
	public void run(){
		while(true){
			if(stopme){
				//退出位置
				pringtln(“exit by stop me!”);
				break;
			}
			//你的代码
		}
	}
}

5 线程中断

  • 什么是线程中断:
    线程中断不会使线程立即退出,而是给线程一个通知,告知目标线程,有人希望你退出了!至于**目标线程接到通知后
    如何处理,由自己决定。**这点很重要,避免了stop()那样出现的问题。
  • 与线程中断的三种方法:
    1 Thread.interrupt():通知目标线程中断,设置中断标志
    2 Thread.isInterrupted():检查中断标志,判断当前线程是否被中断。
    3 Thread.interrupted():判断当前线程中断状态,但同时会清楚当前线程中断标志位状态
    3 如何操作:
main(String[] args){
	Thread t1 = new Thread(){
		public void run(){
			while(true){
				//中断处理程序
				if(Thread.currentThread().isInterrupted()){
					sysou("Interrupted!");
					break;
				}
				...;
				Thread.yield();//线程让步
			}	
		}
	}
	t1.start();
	Thread.sleep(2000);
	t1.interrupt();//单单这里中断是没有效果的,一定要写入上面的中断处理程序
}
  • 中断的方法比stopme()标记手法功能更强劲
  • 强劲之处:在循环体中,出现wait()或sleep(),只能通过中断识别。
    Thread.sleep()方法会抛出一个InterruptedException中断异常,当线程sleep()处于休眠时,如果被中断,异常就会产生。这时会清除中断标记位,如果不处理,在下次循环中无法捕获到这个中断,故在异常处理中,再次设置中断标记位

6 等待(wait)和通知(notify)
两个方法签名

  • 等待(wait)
    当线程A中,调用了obj.wait(),线程A停止继续执行,进入现场等待池中等待,让出系统资源,既让出了锁资源。线程A会一直等到其他线程调用obj.notify()位置。obj对象成为多个线程的有效通信手段。
  • wait 和 notify 如何工作?
    当一个线程调用obj.wait(),那么它就进入obj的等待队列。这个等待队列可能有多个线程。当obj.notify()被调用时,它会从等待队列中水机选选择一个线程,将其唤醒(这里的唤醒是允许其去获得锁)。这个选择时随机的。
  • 除了notify可以唤醒线程,还有notifyAll,只是notifyAll唤醒所有的线程,而不是一个。
  • 注意点
    object.wait()不是随便调用的,必须包含在对应同步语句中,无论是wait 还是 notify都需要首先获得目标对象
    (obj)的一个监视器。
public class SimpleWN{
	public static void main(String[] args) {
		Thread t1 = new T1();
		Thread t2 = new T2();
		t1.start();
		t2.start();
	}
	final static Object obj = new Object();
	
	public static class T1 extends Thread{
		@Override
		public void run() {
			synchronized (obj) {
				System.out.println(System.currentTimeMillis() + ":T1 start! ");
				try {
					System.out.println(System.currentTimeMillis() + ":T1 wait for obj");
					obj.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(System.currentTimeMillis() + ":T1 end!");
			}
			super.run();
		}
	}
	
	public static class T2 extends Thread{

		@Override
		public void run() {
			// TODO Auto-generated method stub
			synchronized (obj) {
				System.out.println(System.currentTimeMillis() + ":T2 start! notify one thread!");
				obj.notify();
				System.out.println(System.currentTimeMillis() + ":T2 end!");
				
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			super.run();
		}
		
	}
}
  • wait 和 sleep :都是等待若干时间,区别是wait可以被唤醒,还有wait方法会释放目标对象的锁。而sleep()不会。

7 挂起(suspend)和继续执行(resume)

  • 被挂起的线程,必须等到resume()操作后,才能继续执行。
  • suspend是被废弃的方法:被挂起的线程在导致线程暂停的同时,并不会释放任何锁资源。导致其他线程想要访问被它暂用的锁资源,都会受牵连。
  • 怎样用一种可靠的方式使用suspend()?
    利用wait()和notify()方法,在应用层实现suspend 和resume 功能。
package com.sean.thread;

public class GoodSuspend{
	public static Object u = new Object();
	
	public static void main(String[] args) throws InterruptedException {
		ChangeObjThread t1 = new ChangeObjThread();
		ReadObjThread t2 = new ReadObjThread();
		t1.start();
		t2.start();
		Thread.sleep(1000);
		t1.suspendMe();
		System.out.println("suspend t1 2 sec");
		Thread.sleep(20000);
		System.out.println("resume t1");
		t1.resumeMe();
	}
	
	public static class ChangeObjThread extends Thread{
		volatile boolean suspendme = false;
		public void suspendMe(){
			suspendme = true;
		}
		
		public void resumeMe() {
			suspendme = false;
			synchronized(this){
				notify();
			}
		}
		@Override
		public void run() {
			while (true) {
				synchronized (this) {
					while (suspendme) {
						try {
							wait();		//运用等待实现挂起
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
				synchronized(u){
					System.out.println("in ChangeObjThread");
				}
				Thread.yield();
			}
		}
	}
	
	public static class ReadObjThread extends Thread{
		
		@Override
		public void run() {
			while(true){
				synchronized (u) {
					System.out.println("in ReadObjThread");
				}
				Thread.yield();
			}
		}
		
	}
}

8 等待线程结束(join)和谦让(yield)

public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException
  • join()表示无限等待,它会一直阻塞当前线程,直到目标线程执行完毕。yield()给出了一个
    最大等待时间,如果超过时间,就会继续往下执行。

  • join()的本质是调用线程wait()在当前线程对象实例上当线程执行完成后,被等待的线程 在退出时调用notifyAll通知所有线程继续执行,因此不要在THread对象上使用类似wait、notify等方法,可能会影响api工作。

  • Thread.yield 一旦执行,会使当前线程让出CPU。但是并不代表不执行,会有一定几率分配到

public class GoodSuspend {
	public volatile static int i = 0;

	public static class AddThread extends Thread {

		@Override
		public void run() {
			for (i = 0; i < 100000000; i++) {

			}
		}
	}

	public static void main(String[] args) throws InterruptedException {
		AddThread at = new AddThread();
		at.start();
		at.join();
		System.out.println(i);
	}
}

3 volatile 与java内存模型(JMM)

1 java内存模型:围绕着原子性、有序性和可见性展开的。但是可以利用关键字或特殊操作,告诉虚拟机有些地方需要特别
注意,比如 volatile。
2 volatile:

  • 使用 volatile 去申明一个变量时,在程序范围内的所有其他线程“可见”的,虚拟机就会小心处理这种情况;
  • volatile并不能代替锁,也无法保证一些复合操作的原子性,当两个线程同时修改数据时,依然会产生冲突
  • volatile能保证数据的可见性有序性
//volatile无法保证一些复合操作
public class GoodSuspend {
	static volatile int i = 0;
	public static class PlusTask implements Runnable{
		@Override
		public void run() {
			for (int k = 0; k < 10000; k++){
				i++;
			};
		}
	}
	public static void main(String[] args) throws InterruptedException {
		Thread[] task = new Thread[10];
		for (int i = 0; i < 10; i++) {
			task[i] = new Thread(new PlusTask());
			task[i].start();
		}
		for (Thread thread : task) {
			thread.join();
		}
		System.out.println(i);
	}
}

4 线程组


  1. 和一个篮子一样,篮子里的每个水果是线程。
public class ThreadGroupName implements Runnable {
	static volatile int i = 0;
	public static void main(String[] args) {
		ThreadGroup tg = new ThreadGroup("PrintGroup");
		Thread t1 = new Thread(tg, new ThreadGroupName(), "T1");	//定义线程并加入到线程组
		Thread t2 = new Thread(tg, new ThreadGroupName(), "T2");
		t1.start();
		t2.start();
		System.out.println(tg.activeCount());
		tg.list();
	}
	@Override
	//输出线程信息
	public void run() {
		String groupAndName = Thread.currentThread().getThreadGroup().getName() + Thread.currentThread().getName();
		while (true) {
			System.out.println("I am group " + groupAndName);
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

5 驻守后台:守护线程(Daemon)


  • 守护线程:系统的守护者,默默完成系统的一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程。与之相对应的是用户线程(系统的工作线程)。
  • 要在start()之前设置守护线程,否则抛出异常,当成用户线程使用。
Thread t = new DaemonT();
t.setDaemon(true);	//设置守护线程
t.start();

6 线程优先级:先做重要的事


  • 优先级越高,资源竞争越有优势,更可能抢占资源。
  • 优先级产生的后果无法精准控制,可能出现低优先级一直抢占不到资源,一直无法运行,从而产生饥饿现象.
  • 自己在应用层解决线程调度的问题。
    • 数字越大优先级越高;有效范围在1-10。
public class PriorityDemo{
	public static void main(String[] args) {
		//高权限和低权限的线程做同一件事
		HightPriority h = new HightPriority();
		LowPriority l = new LowPriority();
		h.setPriority(Thread.MAX_PRIORITY);//线程设置为高权限
		l.setPriority(Thread.MIN_PRIORITY);//线程设置为低权限
		l.start();
		h.start();
	}//~做同一件事情总是高权限线程先完成
	public static class HightPriority extends Thread{
		static int count = 0;
		public void run(){
			while(true){
				synchronized (PriorityDemo.class) {
					//使用同步产生资源竞争,而权限高低会去竞争锁资源
					count++;
					if (count > 1000000) {
						System.out.println("HightPriority is complete!");
						break;
					}
				}
			}
		}
	}
	public static class LowPriority extends Thread{
		static int count = 0;
		public void run(){
			while(true){
				synchronized (PriorityDemo.class) {
					count++;
					if (count > 1000000) {
						System.out.println("LowPriority is complete!");
						break;
					}
				}
			}
		}
	}
}

7 线程安全与synchronized


  • 多线程的写入冲突(线程不安全):当两个线程同时修改一个共享对象,产生覆盖的现象
    比如,有一个共享对象int i,线程A和线程B都对i 累加10000次,我们想要的结果是20000,但我们得到的结果总是小于这个值。

  • 即使用volatile这种关键字也是如此,因为volatile不能解决两个线程同时修改共享对象的问题。

public class AccountingVol implements Runnable{
	public static volatile int i = 0;
	@Override
	public void run() {
		for (int k = 0; k < 1000000; k++) {
			i++;
		}
	}
	public static void main(String[] args) throws InterruptedException {
		AccountingVol v1 = new AccountingVol();
		Thread t1 = new Thread(v1);  //此行和下面一行的Runnable实例一定要是同一个,这样才能保证在两个线程工作时,关注到同一个对象锁上,从而保证线程安全。
		Thread t2 = new Thread(v1);
		t1.start();
		t2.start();
		t1.join();	//主线程愿意等到该线程执行完再执行
		t2.join();
		System.out.println(i);
	}
}
  • 使用 synchronized 来解决线程安全的问题
    • 用法:
      • 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
      • 直接作用于实例对象:相当于对当前实例加锁,…。
      • 直接作用于静态方法:相当于对当前类加锁,进入同步块要获得当前类的锁。
    • 当线程进入被synchronized包裹的代码块,必须先请求锁,如果有其他线程正在持有这把锁,则新到的线程就必须等待。
//将上例中的run加上一个同步块
synchronized (v1) {		//同步块,只有一个持有锁的线程可进入
	for (int k = 0; k < 1000000; k++) {
		i++;
	}
}
  • 可将synchronized关键字作用于一个实例方法:意思是进入该方法前,线程需获得当前对象实例的锁。
//上面同步块代码转为一个方法,这种事获取当前对象实例的锁
public synchronized void increase(){
	i++;
}
for (int k = 0; k < 1000000; k++) {
	this.increase();
}

  • 想要保证两个线程的线程安全,必须要两个线程都指向同一个Runnable实例接口,这样才能使两个线程关注到同一个对象锁上,既要使用同一把锁,才能保证线程安全,使用下面方法也可以正确执行。
//这样使用的是当前类的锁,即使不是同一个Runnable对象,也可以正确执行
public static synchronized void increase(){
	i++;
}

8 程序中的幽灵:隐藏的错误

  • 案例:比如两个数相加导致int的溢出,就会导致出现负数的情况

  • 并发下的 ArrayList:ArrayList 是不安全的,可用 Vector 代替 ArrayList

  • 并罚下的诡异的 HashMap:

    • 可能有三种情况:
      • 1 程序正常结束;2 程序正常结束,不符合预期; 3 程序死循环(链表遭到破坏,链表成环);
  • 错误的加锁:

    • 最好别加在Integer对象上,Integer对象在做++运算时是创建一个新的Integer对象的,String对象也是如此。

猜你喜欢

转载自blog.csdn.net/m0_37505412/article/details/82736899