Thread的三种创建方式
一
public class ThreadExtend extends Thread{ //extends Thread 重写run的方式 private int i=0; @Override public void run(){ for (;i<40;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); } }}public class ThreadRunable implements Runnable{ //implement Thread 重写run的方式 private int i=0; @Override public void run(){ for (;i<40;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); } }}public class MyCallable implements Callable{ //implements Callable的方式 ,使用泛型 返回值为sum private int i=0; @Override public Integer call() throws Exception { //注意使用的是CALL int sum = 0; for (;i<40;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); sum+=i; } // TODO Auto-generated method stub return sum; }}public class Test { public static void main(String[] args) { // TODO Auto-generated method stub Thread threadA = new ThreadExtend();//extends 的线程创建 Runnable runa = new ThreadRunable (); Thread threadB = new Thread(runa);//implements 线程的创建 Callable mycall= new MyCallable(); FutureTask myfuture = new FutureTask(mycall); Thread threadC = new Thread(myfuture);// FutureTask 和Callable 的创建 for (int x=0;x<40;x++){ System.out.println(Thread.currentThread().getName()+" : "+x); if(x==10){ threadA.start(); threadB.start(); threadC.start(); } } System.out.println("循环结束"); try { int sum = myfuture.get();//返回值调用的是myfuture System.out.println("sum="+sum); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
三种方式 其中Runnable 是 实现thread 一个接口的实现,建议使用 Runnable,因为Runnable是一个接口,程序扩展更容易,callalbe 和 futuretask一起使用,优点在于有返回值,其中futuretask的get是在该线程全部执行完毕才会返回。
线程的作用就不再解释了 先看一张经典图
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
就绪状态和运行状态可以转换,运行状态变成就绪状态是使用线程的field方法,Cpu调用就绪状态进入运行状态的时候具有随机性。所以当线程A用field()变成就绪状态的时候,CPU下次还可能随机到它。
package all;public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub Thread threadA = new ThreadExtend(); Thread threadB = new ThreadExtend(); Thread threadC = new Thread(new ThreadRunable()); Thread threadD = new ThreadExtend(); Thread threadE = new ThreadExtend(); Thread threadF = new ThreadExtend(); for (int x=0;x<40;x++){ System.out.println(Thread.currentThread().getName()+" : "+x); if(x==10){ threadA.start(); try { threadA.join();//必须等join执行完毕才能执行main } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }}} for (int x=0;x<40;x++){ System.out.println(Thread.currentThread().getName()+" : "+x); if(x==10){ threadB.start(); try { threadB.sleep(1);//sleep的作用是暂停多少毫秒 暂停时间结束完立即执行 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }} for (int x=0;x<40;x++){ System.out.println("main thread i = " + x); if(x==10){ threadC.setDaemon(true);//设置成后台进程 当前台进程全部结束时才结束后台进程,main是前台进程。 threadC.start(); }} for (int x=0;x<40;x++){ System.out.println(Thread.currentThread().getName()+" : "+x); if(x==10){ threadD.setPriority(Thread.MAX_PRIORITY);//设置线程优先级 ,只是机会比较多,其中最高是10 最低是1 main是5 threadD.start(); }} try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } threadE.setPriority(Thread.MIN_PRIORITY); threadF.setPriority(Thread.MAX_PRIORITY); for (int x=0;x<40;x++){ System.out.println(Thread.currentThread().getName()+" I : "+x); if(x==5){ threadE.start(); threadF.start(); } if (x==30){ System.out.println("yield"); Thread.yield();//yield 当yield 使当前程序进入就绪状态 , 只会执行priorit比当前大或者等于的执行绪 ,在这里它会执行threadF ,但是好像没有实现具体再看。 } } }}
1.join()
join —— 让一个线程等待另一个线程完成才继续执行。如A线程线程执行体中调用B线程的join()方法,则A线程被阻塞,知道B线程执行完为止,A才能得以继续执行。
2.sleep()
sleep —— 让当前的正在执行的线程暂停指定的时间,并进入阻塞状态。在其睡眠的时间段内,该线程由于不是处于就绪状态,因此不会得到执行的机会。即使此时系统中没有任何其他可执行的线程,出于sleep()中的线程也不会执行。因此sleep()方法常用来暂停线程执行。
3.后台线程(Daemon Thread)
概念/目的:后台线程主要是为其他线程(相对可以称之为前台线程)提供服务,或“守护线程”。如JVM中的垃圾回收线程。
生命周期:后台线程的生命周期与前台线程生命周期有一定关联。主要体现在:当所有的前台线程都进入死亡状态时,后台线程会自动死亡。
4.改变线程的优先级/setPriority():
每个线程在执行时都具有一定的优先级,优先级高的线程具有较多的执行机会。每个线程默认的优先级都与创建它的线程的优先级相同。main线程默认具有普通优先级。
设置线程优先级:setPriority(int priorityLevel)。参数priorityLevel范围在1-10之间,常用的有如下三个静态常量值:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
获取线程优先级:getPriority()。
5.线程让步:yield()
上一篇博文中已经讲到了yield()的基本作用,同时,yield()方法还与线程优先级有关,当某个线程调用yiled()方法从运行状态转换到就绪状态后,CPU从就绪状态线程队列中只会选择与该线程优先级相同或优先级更高的线程去执行
线程同步和锁
一 在需要同步的数据或者方法前面加synchronized
package all;public class User { private String name; private int account;public User(String name ,int count){ this.name=name; this.account=count;}public String getName() { return name;}public void setName(String name) { this.name = name;}public int getAccount() { return account;}public void setAccount(int account) { this.account = account;}public synchronized void addMoney(int number){ //synchronized标识符也可以在代码段的前面,好处是节省性能 account+=number; System.out.println("User:"+this.name+" Account="+this.account);}public void subMoney(int number){ account-=number; System.out.println("User:"+this.name+" Account="+this.account);}public void say(){ System.out.println("剩余钱"+this.account);}}package all;public class ThreadUse extends Thread{ private User user; public ThreadUse(String name,User user){ //user是我们要共同操作的对象 super(name);//给线程名字 this.user=user; } public void run(){ for(int x=0;x<10000000;x++){ user.addMoney(1); } } }package all;public class Test3 { public static void main(String[] args) { // TODO Auto-generated method stub User user = new User("xiaozhi",0); Thread t1 = new ThreadUse("线程一",user); Thread t2 = new ThreadUse("线程二",user); t1.start(); t2.start(); try { Thread.sleep(2000);//加这个是让线程全部跑完,不然线程一可能会提前结束,输出不会是20000000 user.say();//可以将之前的synchronized 会发现并不会输出20000000。 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
另一种方法,使用对象锁模式 利用 对象的wait() notisfy(),首先明白任何对象都有“锁”,将之应用到生产者和消费者模式,其中User就是上个例子的User
package all;public class Customer extends Thread { private User user ; public Customer (String name,User user){ super(name); this.user=user; } public void run(){ while (true){ synchronized (user){ //关键字里面加入的是我们需要判断的对象锁 while (user.getAccount()<=0){ //为什么用while不用if ,因为可能有不相关的其他线程启动notisyAll(),会启动现在在wait()的线程,然而如果用if他不会再判断是否条件满足,傻傻的就run下去了 try { System.out.println(Thread.currentThread().getName()+":wait()"); user.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } user.subMoney(1); user.notifyAll(); } } }}package all;public class Producer extends Thread { private User user; public Producer (String name , User user){ super (name); this.user=user; } public void run(){ while (true){ synchronized (user){ while (user.getAccount()>=1000){ try { System.out.println(Thread.currentThread().getName()+":wait()"); user.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } user.addMoney(1); user.notifyAll(); } } } }package all;public class Test3 { public static void main(String[] args) { // TODO Auto-generated method stub User user = new User ("xiaozhi",20); Customer cus = new Customer ("消费者",user); Producer pro = new Producer ("生产者",user); cus.start(); pro.start(); }} 现在学习 Lock lock 和 synchronized 的区别在于前者是类级别 后者是 语法级别,lock 操作更加复杂 需要手动 ulock ,可以设置公平还是非公平解锁,操作更复杂带来的是更加灵活的控制。
import java.util.concurrent.locks.ReentrantLock;public class LockBuffer { private ReentrantLock lock = new ReentrantLock();//Lock接口的实现,ReentrantLock 。 public void wirter () { lock.lock(); try{ while(true){ //假设这种操作很长时间,用来测试reentrantlock的中断测试 try { Thread.sleep(100); System.out.println("i am writter.");//模拟一种写操作 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }finally{ //要用finally来解锁,否则上面获取锁后出现错误了就不会进行解锁。 lock.unlock(); } } public void read () throws InterruptedException { lock.lockInterruptibly();// 注意这里,可以响应中断 try{ System.out.println("Read!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.");//模拟一种读操作 }finally{ lock.unlock(); } }}public class Writer extends Thread{ private LockBuffer lb; public Writer (LockBuffer lb){ this.lb=lb; } @Override public void run (){ System.out.println("Begging writer now!"); lb.wirter(); }}public class Read extends Thread{ private LockBuffer lb; public Read (LockBuffer lb){ this.lb=lb; } @Override public void run (){ System.out.println("Begging read now!"); try { lb.read(); } catch (InterruptedException e) { //用于测试 ReentrantRead intterrupt的特性 // TODO Auto-generated catch block System.out.println("我不读了"); } }public class Test4 { public static void main(String[] args) throws Throwable { // TODO Auto-generated method stub LockBuffer lb = new LockBuffer (); Read rd = new Read (lb); Writer wt = new Writer (lb); wt.start(); Thread.sleep(100); rd.start();//本来 是要等上面结束是吧 new Thread(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); for (;;) { if (System.currentTimeMillis()- start > 5000) { System.out.println("不等了,尝试中断"); rd.interrupt();//获取中断异常,之后对异常进行出来,就是说,老子不读了! break; } } }}).start(); }}}
接下来看看 ReenReadWriterLock 的使用
import java.util.concurrent.locks.ReentrantReadWriteLock;public class LockBuffer { private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void wirter () { lock.writeLock().lock();; try{ System.out.println("Begging writer now!"); while(true){ //假设这种操作很长时间 try { Thread.sleep(300); System.out.println("i am writter.");//模拟一种写操作 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }finally{ lock.writeLock().unlock(); } } public void read () throws InterruptedException { lock.readLock().lock(); try{ System.out.println("Begging read now!"); while(true){ //假设这种操作很长时间 try { Thread.sleep(300); System.out.println("i am read.");//模拟一种读操作 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }finally{ lock.readLock().unlock(); } }}public class Writer extends Thread{ private LockBuffer lb; public Writer (LockBuffer lb){ this.lb=lb; } @Override public void run (){ lb.wirter(); }}public class Read extends Thread{ private LockBuffer lb; public Read (LockBuffer lb){ this.lb=lb; } @Override public void run (){ try { lb.read(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}public class Test4 { public static void main(String[] args) throws Throwable { // TODO Auto-generated method stub LockBuffer lb = new LockBuffer (); Read rd = new Read (lb); Read rd2 = new Read (lb); Writer wt = new Writer (lb); Writer wt2 = new Writer (lb); rd.start(); rd2.start();// wt.start();// wt2.start();// 运行两个rd 会发现,因为是读操作,可以两个都运行,但是运行两个wt,只有一个会运行,另一个一直在等待。其余特性可以自己测试 }}