java多线程学习
参考:
http://blog.csdn.net/stellaah/article/details/6798244
http://bbs.csdn.net/topics/300116354
http://blog.csdn.net/ghsau/article/details/7421217
http://blog.csdn.net/sd0902/article/details/8395677
创建线程:
创建线程过程:
1.继承Thread
class 类名A extends Thread { public void run(){} } 2.实例对象 Thread t=new 类名A(); 3.启用线程 t.start();自动会去运行run().
2.Runable接口
实现Runnable接口创建线程 1.实现Runnable接口 类名A implements Runnable() { public void run(){} } 2.启用线程 new Thread(类名A的对象).start();
区别:
使用Runnable接口创建多线程,适合多个相同的程序代码的线程去处理分享同一个资源的情况,把虚拟CPU(线程)同程序的代码数据有效分离,较好体现了面向对象的设计思想.
setDeamon(true)
设置守护线程,程序非守护线程全部退出时,守护线程自动退出。
join(),join(msecond) 合并线程(如何实现一个线程执行中让另一个线程先执行然后再继续执行自己)
t1.join(500) 打断当前线程执行t1线程,500msecond 如果t1线程死亡 则直接继续执行当前线程
package com.thread; public class JoinThread { public static void main(String[]args) { Thread o=new OneThread(); // o.start(); int index=0; while(true) { System.out.println("main->>"+index+" "+Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } if(index++==3) { try { o.start(); o.join();//把o对应的线程合并10000毫秒 } catch(Exception e) { e.printStackTrace();//输出导致异常更为详细的信息 } } } } } class OneThread extends Thread { public void run() { int i = 1; while(++i<10) { System.out.println("OneThread->>"+Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } } /* 运行的结果: 先是两个线程交替执行,main()方法的线程被执行30次后执行run方法的线程. run方法被执行10000毫秒后就变为刚开始时的样子---两个线程交替执行. */
Yield()
在多线程程序中,为了防止某线程独占CPU资源(这样其它的线程就得不到"响应"了).可以让当前执行的线程"休息"一下.但是这种thread.yield() 调用,并不保证下一个运行的线程就一定不是该线程.
public class Test extends Thread { public static void main(String[] args) { for (int i = 1; i <= 2; i++) { new Test().start(); } } public void run() { System.out.print("1"); yield(); System.out.print("2"); } } 输出结果: 1122 或者 1212
synchronized
synchronized代码块的用法-synchronized(Object){}
synchronized方法的用法需要与run()方法两个结合起来用
wait() notify
wait()休眠放弃资源控制锁
notify 唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。
notifyAll() 唤醒在此对象监视器上等待的所有线程
Timer TimerTask
自JDK5之后,可以用ScheduledThreadPoolExecutor来替代Timer。
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);// 效果类似于Timer定时器
import java.util.*; class ZhaDan extends TimerTask //炸弹 { public void run() { System.out.println("Boang... ...炸了!"); } } class TimerTest { public static void main(String[]args) { Timer t=new Timer();//创建定时器对象 ZhaDan z=new ZhaDan();//创建炸弹对象 t.schedule(z,1000,2000);//隔1秒引爆炸弹.之后就是每2秒一炸 while(true) { try{Thread.sleep(1000);}catch(Exception e){} System.out.println(new Date().getSeconds());//输出当前时间的秒 } } }
中断线程
while(flag)
用thread.interrupt()
或ThreadPoolExecutor.shutdown()
package cn.com.interrupt; import java.util.Scanner; public class Test extends Thread { private String name; public Test(String name) { this.name = name; } public void write() { System.out.print(name); } public static void main(String[]args) throws Exception{ Thread t1=new Test("myThread"); t1.start(); Scanner sc=new Scanner(System.in); String s; Thread.sleep(5000); t1.interrupt(); } public void run() { for(int i=0;;i++){ if(this.isInterrupted()){ System.out.println("exit*****************"); return; } write(); System.out.println(); } } }
线程同步问题首先要说明Java线程的两个特性:可见性,有序性
每个线程都有自己的工作内存(线程栈),工作内存存储了主内存Count对象的一个副本,当线程操作Count对象时,首先从主内存复制Count对象到工作内存中,然后执行代码count.count(),改变了num值,最后用工作内存Count刷新主内存Count。当一个对象在多个内存中都存在副本时,如果一个内存修改了共享变量,其它线程也应该能够看到被修改后的值,此为可见性
多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程,一个最经典的例子就是银行汇款问题,一个银行账户存款100,这时一个人从该账户取10元,同时另一个人向该账户汇10元,那么余额应该还是100。那么此时可能发生这种情况,A线程负责取款,B线程负责汇款,A从主内存读到100,B从主内存读到100,A执行减10操作,并将数据刷新到主内存,这时主内存数据100-10=90,而B内存执行加10操作,并将数据刷新到主内存,最后主内存数据100+10=110,显然这是一个严重的问题,我们要保证A线程和B线程有序执行,先取款后汇款或者先汇款后取款,此为有序性
synchronized volitile
synchronized:
1.修饰变量
sychronized(obj){
}
2.修饰方法
void sychronized funA(){
}
每个锁对都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个线程被唤醒(notify)后,才会进入到就绪队列,等待CPU的调度,反之,当一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒,这个涉及到线程间的通信,当第一个线程执行输出方法时,获得同步锁,执行输出方法,恰好此时第二个线程也要执行输出方法,但发现同步锁没有被释放,第二个线程就会进入就绪队列,等待锁被释放。一个线程执行互斥代码过程如下:
1. 获得同步锁;
2. 清空工作内存;
3. 从主内存拷贝对象副本到工作内存;
4. 执行代码(计算或者输出等);
5. 刷新主内存数据;
6. 释放同步锁。
所以,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。
volitile:
修饰变量 不能和final一起修饰
static volatile int i = 0, j = 0;
volatile可以将共享变量的改变直接响应到主内存中,这样保证了主内存中的值一致性可以保证内存可见性,不能保证并发有序性
另 volatile 修饰函数 表示编译器不进行代码优化
线程池
创建一个可重用固定线程集合的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
创建一个可根据需要创建新线程的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程
ExecutorService threadPool = Executors.newSingleThreadExecutor();
创建一个可安排在给定延迟后运行命令或者定期地执行的线程池。
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);
Future Callable
http://openhome.cc/Gossip/DesignPattern/FuturePattern.htm
Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值
Future模式在請求發生時,會先產生一個Future物件給發出請求的客戶,而同時間,真正的目標物件之生成,由一個 新的執行緒持續進行(即 Worker Thread),真正的目標物件生成之後,將之設定至Future之中,而當客戶端真正需要目標物件時, 目標物件也已經準備好,可以讓客戶提取使用。
Lock 锁
更广泛,继承类更多
private Lock lock = new ReentrantLock()
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
public class LockTest { public static void main(String[] args) { final Outputter1 output = new Outputter1(); new Thread() { public void run() { output.output("zhangsan"); }; }.start(); new Thread() { public void run() { output.output("lisi"); }; }.start(); } } class Outputter1 { private Lock lock = new ReentrantLock();// 锁对象 public void output(String name) { // TODO 线程输出方法 lock.lock();// 得到锁 try { for(int i = 0; i < name.length(); i++) { System.out.print(name.charAt(i)); } } finally { lock.unlock();// 释放锁 } } }
死锁实例::
package com.test; public class SynchronizedSisuo { /** * @param args */ public static void main(String[] args) { SynchronizedSisuo ss = new SynchronizedSisuo(); ss.t1.start(); } Thread t1 = new Thread(new Runnable() { @Override public void run() { t1Run(); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { t2Run(); } }); Object obj = new Object(); public void t1Run() { synchronized (obj) { System.out.println("1111111>>>pre"); t2.start(); try { t2.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("1111111>>>pos"); } } public void t2Run() { synchronized (obj) { System.out.println("2222222>>>pre"); System.out.println("2222222>>>pos"); } } }