java 多线程笔记

多线程

cpu只能干一件事,好像一次干很多次,是因为处理速度很快,一边一点的做 ,切换着做,就是多线程

进程和线程

  1. 进程process是操作系统的一个任务,有一块独立的内存

  2. 线程是进程的几个任务,process创建,os会自动申请主线程

并发和并行

  1. 并发

通过cpu调度算法,多个线程并发运行,不是同时运行 ,OS划分时间,每个进程轮流着干,用TPS或者QPS来反应这个系统的处理能力

  1. 并行

多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。

线程生命周期

new => runnable => run =>dead
runnable 和run之间有block

Thread 在lang包中

不可能切换一次一次

Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
t1.start();//启动用start,进入runnale状态,一旦获得cpu时间就会运行 
t2.start();
class Thread1 extends Thread {//run是干的活
	public void run(){
		for(int a =0;a<3;a++) {
			System.out.println("hello");
		}
	}
}

class Thread2 extends Thread {
	public void run(){
		for(int a =0;a<3;a++) {
			System.out.println("Hi");
		}
	}
}

上面写法的不足与改进(实现runnable接口,重写run)

  1. java单继承,继承了thread后无法继承其他类
  2. 执行的run和线程有耦合关系,重用性不好
Thread3 task3 = new Thread3();
Thread4 task4 = new Thread4();

Thread t3 = new Thread(task3);
Thread t4 = new Thread(task4);

t3.start();
t4.start();

class Thread3 implements Runnable {
	public void run() {
		for(int a =0;a<3;a++) {
			System.out.println("Hi");
		}
	}
}

class Thread4 implements Runnable {
	public void run() {
		for(int a =0;a<3;a++) {
			System.out.println("hallo");
		}
	}
}

匿名内部类写法

new Thread(new Runnable() {
	public void run() {
		for(int a =0;a<3;a++) {
			System.out.println("I am huahuadavids");
		}
	}
}).start();

查看线程信息 Thread.currentThread();

返回在方法中的线程

Thread main = Thread.currentThread();
System.out.println(main);
// Thread[main,5,main] 名字优先级方法

System.out.println(main.getId());// 线程id

System.out.println(main.getName());//线程名字

线程优先级 1-10(10最高)

线程的时间片是由线程调用被动的,理论上,优先级越高,获取时间片的次数越多

p(Thread.MIN_PRIORITY);// 1 
p(Thread.NORM_PRIORITY);// 5 
p(Thread.MAX_PRIORITY);// 10 

Thread t1 = new Thread(new Runnable() {
	public void run() {
		for(int i=0;i<10000;i++) {
			p("t11111");
		}
	}
});
Thread t2 = new Thread(new Runnable() {
	public void run() {
		for(int i=0;i<10000;i++) {
			p("t22222");
		}
	}
});
Thread t3 = new Thread(new Runnable() {
	public void run() {
		for(int i=0;i<10000;i++) {
			p("t33333");
		}
	}
});
t1.setPriority(1);
t2.setPriority(5);
t3.setPriority(10);
t1.start();
t2.start();
t3.start();

sleep

让线程进入阻塞,超时后进入runnale状态

Thread rose = new Thread(new Runnable() {
	public void run() {
		for(int a=0;a<10;a++) {
			System.out.println("I will jump");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("I jump !!! ");
	}
});

守护线程

  1. 守护线程是后台线程
  2. 进程的前台线程都结束,后台线程也必须结束
  3. 当进程只剩下守护线程,所有程序线程必须over
  4. GC就是守护线程
Thread rose = new Thread(new Runnable() {
	public void run() {
		for(int a=0;a<10;a++) {
			System.out.println("I will jump");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("I jump !!! ");
	}
});

Thread jack = new Thread(new Runnable() {
	public void run() {
		while(true) {
			System.out.println("You jump, I jump");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}
});
jack.setDaemon(true);
rose.start();
jack.start();
System.out.println("main结束");
  1. main的线程首先结束
  2. rose线程结束
  3. jack结束
  4. GC是死循环
  5. yield方法 主动让出cpu时间,进入Runnable

join方法

join方法可以让他的线程进入block,一直到该方法的线程完成工作,一般用作多个线程的同步工作

//在匿名内部类中,使用保存匿名内部类的变量,必须是final 
public static boolean isFinish = false;
public static void main(String[] args) {
	final  Thread download = new Thread() {
		public void run() {
			System.out.println("下载开始");
			for(int a =0;a<101;a+=10) {
				System.out.println("正在下载:"+ a+"%");
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println("下载结束");
			isFinish = true;
		}
	};
		
	Thread show = new Thread(new Runnable() {
		public void run() {
			
			try {
				download.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("开始加载图片");
			if(!isFinish) {
				throw new RuntimeException("图片未下载完毕");
			}
			System.out.println("结束加载图片");
		}
	});
	download.start();
	show.start();
}

线程安全

线程的调度顺序不影响任何结果 ,cpu调用时间的不确定造成了结果的可能问题,造成了抢资源 ,java多线程导致的安全性问题,java中的锁机制是解决这个问题,就是排队,不利于效率

解决线程安全问题

加锁

  1. 锁能使其保护的代码以串行的形式来访问,当给一个复合操作加锁后,能使其成为原子操作。一种错误的思想是只要对写数据的方法加锁,其实这是错的,对数据进行操作的所有方法都需加锁,不管是读还是写。

  2. 加锁时需要考虑性能问题,不能给整个方法加锁synchronized就了事了,应该将方法中不影响共享状态且执行时间比较长的代码分离出去。

  3. 加锁的含义不仅仅局限于互斥,还包括可见性。为了确保所有线程都能看见最新值,读操作和写操作必须使用同样的锁对象。

不共享状态

  1. 无状态对象:无状态对象一定是线程安全的,因为不会影响到其他线程。
  2. 线程关闭: 仅在单线程环境下使用。

不可变对象

使用final修饰的对象保证线程安全,由于final修饰的引用型变量(除String外)不可变是指引用不可变,但其指向的对象是可变的,所以此类必须安全发布,也即不能对外提供可以修改final对象的接口

线程里的run方法,抛出一个异常到run之外,这个线程就被干掉了

线程安全demo

class Table {
	private int beans = 20;
	public int getBeans() {
		if(beans == 0) {
			throw new RuntimeException("no beans");
		}
		Thread.yield();
		return beans--;
	}
}

final Table table = new Table();
Thread p1 = new Thread() {
	public void run() {
		while(true) {
			int bean = table.getBeans();
			Thread.yield();
			System.out.println(getName() + " " +  bean);
		}
	}
};

Thread p2 = new Thread() {
	public void run() {
		while(true) {
			int bean = table.getBeans();
			Thread.yield();
			System.out.println(getName() + " " +  bean);
		}
	}
};
p1.start();
p2.start();
/*
Thread-0 -264733
Thread-0 -264734
Thread-0 -264735
Thread-0 -264736
Thread-0 -264737
Thread-0 -264738
Thread-0 -264739
*/

synchronized

把对象上锁,只有钥匙才能进 ,否则就在阻塞 ,就是把抢资源变成排队获取资源

class Table {
	private int beans = 20;
	public synchronized int  getBeans() {
		if(beans == 0) {
			throw new RuntimeException("no beans");
		}
		Thread.yield();
		return beans--;
	}
}

同步块

缩小范围,提高效率

  1. 原来的
class shop {
	public  synchronized void buy() {
		try {
			Thread t = Thread.currentThread();
			System.out.println(t.getName() + "进入商店");
			Thread.sleep(3000);
			System.out.println(t.getName() + "进入试衣间");
			Thread.sleep(3000);
			System.out.println(t.getName() + "付款离开");
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
}

shop shop = new shop();

Thread t1 = new Thread() {
	public void run() {
		shop.buy();
	}
};
Thread t2 = new Thread() {
	public void run() {
		shop.buy();
	}
};

t1.start();
t2.start();
  1. 改进的
class shop {
	public  void buy() {
		try {
			Thread t = Thread.currentThread();
			System.out.println(t.getName() + "进入商店");
			Thread.sleep(3000);
			synchronized(this) {
				System.out.println(t.getName() + "进入试衣间");
				Thread.sleep(3000);
			}
			System.out.println(t.getName() + "付款离开");
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
}

同步和异步

线程排队就是同步,反之亦然

静态方法的同步,一定有效果

静态方法的同步,一定有效果 和对象无关

互斥锁

  1. synchronized叫同步锁,也叫互斥锁
  2. 修饰方法就是锁这个的对象
  3. 同步锁,2个线程不可以同一段代码
  4. 互斥锁, synchronized修饰的代码不同
class boo {
	public synchronized void ma() {
		try {
			Thread t = Thread.currentThread();
			String name = t.getName();
			System.out.println(name + "在执行A方法");
			Thread.sleep(2000);
			System.out.println(name + "结束执行A方法");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	public void mb() {
		try {
			Thread t = Thread.currentThread();
			String name = t.getName();
			System.out.println(name + "在执行b方法");
			Thread.sleep(2000);
			System.out.println(name + "结束执行b方法");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}


boo obj = new boo();

Thread t1 = new Thread() {
	public void run() {
		obj.ma();
	}
};

Thread t2 = new Thread() {
	public void run() {
		obj.ma();
	}
};

t1.start();
t2.start();



线程安全和集合

  1. vector是同步的
  2. ArrayList,linkedlist和hashSet不是同步的
  3. 就算是线程安全的集合,对于集合的操作和遍历,不是互斥的,需要自行维护
List<String> list = new ArrayList<String>();
list.add("one");
list.add("one1");
list.add("one");
System.out.println(list);
list = Collections.synchronizedList(list);
System.out.println(list);

Set<String> set = new HashSet<String>(list);
set = Collections.synchronizedSet(set);

Map<String,Integer> map = new HashMap<String,Integer>();
map.put("math", 100);
map = Collections.synchronizedMap(map);

线程池

  1. 控制线程数量
  2. 重用线程
  3. 大量使用线程和频繁创建销毁线程

固定大小的线程池

固定大小,多余的分配的线程在队列中

  1. showdown 把线程池中缓存的任务都干完再停
  2. shoudownNow 马上停

blockingQueue 双缓冲队列

解决了队列的安全问题,效率也可以

posted @ 2017-10-25 15:39  StarBugs  阅读(164)  评论(0编辑  收藏  举报