进程和线程
进程:是指运行中的应用程序,每一个进程都有自己独立的内存空间,一个应用程序可以启动多个进程,例如打开IE浏览器,每打开一个IE浏览器窗口,就启动了一个新的进程。
线程: 相当于进程的子进程,它是进程中独立运行的子任务,线程是一种轻量级的进程,一个进程中可以运行许多不同的线程,这些线程可能业务相同,也可能不同,当进程内的多个线程同时运行时,这种方式称为并发执行。
采用多线程的方法可以同时完成几件事情而互不干扰,并发所指的是只有一个CPU执行多个任务,这里的“同时”其实并不是在同时执行,而是多个线程轮换执行,只是由于CPU的执行速度实在太快了,给我们的感觉好象是在同时进行。并行才是真正同时执行任务的,因为它就是多个cpu在同时执行多个任务。
线程的实现方式
1、继承java.lang.Thread类
package com.part01; public class Demo01 { static class Mythread extends Thread { public Mythread() { super("Mythread"); } @Override public void run() { for(int i=1000;i<2000;i++) { //CPU执行到此行指令,拿到当前代码所在的线程对象 System.out.println(Thread.currentThread().getName()+"********"+i); //System.out.println(this.getName()+"********"+i); } } } public static void main(String[] args) { new Mythread().start(); for(int i=0;i<1000;i++) { System.out.println(Thread.currentThread().getName()+"--------"+i); } } }
2、实现java.lang.Runnable接口
package com.part01; class MyThread implements Runnable { private String name; public MyThread(String name) { this.name=name; } @Override public void run() { for(int i=1000;i<2000;i++) { System.out.println(this.name+"--->"+i); } } } public class Demo02 { public static void main(String[] args) { MyThread mt1=new MyThread("线程A"); MyThread mt2=new MyThread("线程B"); MyThread mt3=new MyThread("线程C"); new Thread(mt1).start(); new Thread(mt2).start(); new Thread(mt3).start(); } }
3、实现java.util.concurrent.Callable接口
package com.part01; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class add implements Callable<Integer> { @Override public Integer call() throws Exception { int sum=0; for(int i=0;i<=100;i++) { sum+=i; System.out.println(Thread.currentThread().getName()+"---"+sum); } return sum; } } public class Demo03 { public static void main(String[] args) throws Exception { FutureTask<Integer> ft=new FutureTask<Integer>(new add()); Thread t1=new Thread(ft,"求和"); t1.start(); for(int i=1000;i<2000;i++) { System.out.println("main------------->"+i); } int re=ft.get();//拿到Call执行的结果 System.out.println(re); } }
线程的生命周期
1、创建状态 Thread t=new Thread() 此时它已经拥有相应的内存空间和资源,但还处于不可运行状态 。
2、可运行状态 新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,就会进入就绪状态。此时线程进入线程队列进行排队,等待CPU服务,这表明它已经具备了运行条件。
3、运行状态 当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时自动调用该线程对象的run()方法。run()方法代码段里就是线程的具体任务。
4、堵塞状态 线程在可运行状态或运行状态下都可以进入阻塞状态,如调用sleep()、wait()、join()等方法。
5、终止状态
(1)使用退出标志,使线程正常退出,即run()方法完成后线程终止
(2)有异常
(3)return
(4)stop()【不推荐】/interrupt()
处于终止状态的线程不具有继续运行的能力
线程的控制
Thread.sleep() 线程休眠 将线程变为阻塞状态 时间到期变为可运行状态
Thread.yeild() 线程让步 放弃当前的CPU资源,将它让给其他任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上就又获得CPU时间片。
Thread.join() 线程合并
sk.start();
sk.join();//合并 A线程调B线程的join(),那么A要等待B的代码先执行完再执行A。package com.part01; /** * 线程合并,A线程调B线程的join(),那么A要等待B的代码先执行完再执行A。 *main()线程调用上课和吃饭,那么上课和吃饭执行完再执行main()剩下的任务。 */ public class Demo05 { public static void main(String[] args)throws Exception { for(int i=0;i<100;i++) { if(i==20) { Shangke sk=new Shangke(); sk.start(); sk.join();//合并 } if(i==30) { Chi chi=new Chi(); chi.start(); chi.join(); } System.out.println(Thread.currentThread().getName()+"++++++++++++ "+i); } } public static class Shangke extends Thread { public Shangke() { super("上课"); } @Override public void run() { for(int i=0;i<100;i++) { System.out.println(this.getName()+"------"+i); } } } public static class Chi extends Thread { public Chi() { super("吃饭"); } @Override public void run() { for(int i=0;i<100;i++) { System.out.println(this.getName()+"------"+i); } } } }
Thread.interrupt() 意思是是否被打断,默认情况下返回false。
Thread.interrupted()
Thread.isInterrupted()
守护线程
服务于其他线程的线程,如果它服务的线程死亡,那么它也自动死亡
de.setDaemon(true);//设为守护线程
de.start();package com.part01; /** * 守护线程 * 服务于其它线程的线程,如果其它线程都死亡了,它也自然死亡 gc */ public class Demo06 { public static void main(String[] args) { DemThread de=new DemThread(); de.setDaemon(true);//设为守护线程 de.start(); for(int i=0;i<100;i++) { System.out.println(Thread.currentThread().getName()+"----"+i); } } static class DemThread extends Thread { public DemThread() { super("aaa"); } @Override public void run() { for(int i=0;i<1000;i++) { System.out.println(this.getName()+"----"+i); } } } }
线程优先级
sk.setPriority(10);
sk.start();在操作系统中,线程可以划分优先级,线程的优先级为1-10,值越大优先级越高。优先级较高的线程得到的CPU资源多一些,也就是CPU有可能优先执行优先级较高的线程对象中的任务。
public final void setPriority(int newPriority)//设置线程优先级 public final int getPriority();//取得线程优先级
Thread th=new Thread(); th.setPriority(Thread.MAX_PRIORITY);//修改一个线程对象的优先级为最大优先级
主方法也是一个线程,它的优先级是数字为5的优先级,即中等优先级NORM_PRIORITY。
线程的同步
多线程共享数据,不能是基本数据类型,因为方法传参传的是值的副本,这样达不到共享数据的目的,所以被共享对象一定要用对象类型。对象类型是个指针,指向堆里面的内存,指针复制几次都指向堆里的同一块内存。
package com.part01; /** * 多线程共享数据所共享的数据不能是基本类型 */ public class Demo07 { public static void main(String[] args) { //这个程序是错的,方法传参传是值的副本 int counter=100;//100张票 Saler s1=new Saler("北站", counter); Saler s2=new Saler("南站", counter); s1.start(); s2.start(); } static class Saler extends Thread { int count=0; public Saler(String name,int count) { super(name); this.count=count; } @Override public void run() { while(count>1) { count--; System.out.println(this.getName()+"卖出一张票---,还剩..."+count); } } } }
多线程共享变量,可能会存在数据安全问题,一定要对临界资源上锁。(该程序只是完成了数据共享)
package com.part01; /** * 被共享对象一定要用对象类型 * 可以完成共享,但是没有对临界资源进行上锁 */ public class Demo08 { public static void main(String[] args) { //方法传参传的是值的副本,因为值是一个对象的指针 TicketBox tb=new TicketBox("k32", 100); Saler s1=new Saler("北站", tb); Saler s2=new Saler("南站", tb);; s1.start(); s2.start(); } /** * 被共享的对象 * @author Administrator * */ static class TicketBox { private int counter; private String name; public TicketBox(String name,int counter) { this.name=name; this.counter=counter; } public int getCounter() { return counter; } public void setCounter(int counter) { this.counter = counter; } public String getName() { return name; } public void setName(String name) { this.name = name; } } static class Saler extends Thread { TicketBox tb; //name 就是线程的名字,南站与北站 public Saler(String name,TicketBox tb) { super(name); this.tb=tb; } @Override public void run() { while(true) { //以下代码段,当线程a 还没有执行完时,不能让b执行 if(tb.getCounter()>0) { tb.setCounter(tb.getCounter()-1); System.out.println(this.getName()+"--卖了一张票,还有.."+tb.getCounter()); }else { break; } try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
java中的每个对象都有一个同步监视器 ,java中的每个对象都有一把锁。
同步代码块锁:synchronized(锁定对象){代码}
package com.part01; /** * java中的每个对象都有一个同步监视器 ,java中的每个对象都有一把锁 */ public class Demo09{ public static void main(String[] args) { //方法传参传的是值的副本,因为值是一个对象的指针 TicketBox tb=new TicketBox("k32", 100); Saler s1=new Saler("北站", tb); Saler s2=new Saler("南站", tb);; s1.start(); s2.start(); } /** * 被共享的对象 */ static class TicketBox { private int counter; private String name; public TicketBox(String name,int counter) { this.name=name; this.counter=counter; } public int getCounter() { return counter; } public void setCounter(int counter) { this.counter = counter; } public String getName() { return name; } public void setName(String name) { this.name = name; } //卖票 public void sale() { if(counter>0) { counter--; System.out.println(Thread.currentThread().getName()+" 卖出一张票,还有"+counter); } } } static class Saler extends Thread { TicketBox tb; public Saler(String name,TicketBox tb) { super(name); this.tb=tb; } @Override public void run() { while(true) { //找共享对象的锁 synchronized (tb) { tb.sale(); if(tb.getCounter()<1) break; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
或者还可以变形成这样。
package com.part01; /** * java中的每个对象都有一个同步监视器 ,java中的每个对象都有一把锁 */ public class Demo10 { public static void main(String[] args) { //方法传参传的是值的副本,因为值是一个对象的指针 TicketBox tb=new TicketBox("k32", 100); Saler s1=new Saler("北站", tb); Saler s2=new Saler("南站", tb);; s1.start(); s2.start(); } /** * 被共享的对象 */ static class TicketBox { private int count; private String name; public TicketBox(String name,int count) { this.name=name; this.count=count; } public int getcount() { return count; } public void setcount(int count) { this.count = count; } public String getName() { return name; } public void setName(String name) { this.name = name; } //卖票 public void sale() { synchronized (this) //this是当前对象,是TicketBox { if(count>0) { count--; System.out.println(Thread.currentThread().getName()+" 卖出一张票,还有"+count); } } } } static class Saler extends Thread { TicketBox tb; public Saler(String name,TicketBox tb) { super(name); this.tb=tb; } @Override public void run() { while(true) { tb.sale(); if(tb.getcount()<1) break; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
同步方法锁:在方法上声明一个synchronized 关键词。就相当于用当前对象锁方法代码段。
public synchronized void sale()
效果等同于Demo10
Lock锁: ReentrantLock lock=new ReentrantLock() lock.lock() lock.unlock()
package com.part01; import java.util.concurrent.locks.ReentrantLock; public class Demo11 { public static void main(String[] args) { //方法传参传的是值的副本,因为值是一个对象的指针 TicketBox tb=new TicketBox("k32", 100); Saler s1=new Saler("北站", tb); Saler s2=new Saler("南站", tb);; s1.start(); s2.start(); } static class TicketBox { private int counter; private String name; public TicketBox(String name,int counter) { this.name=name; this.counter=counter; } public int getCounter() { return counter; } public void setCounter(int counter) { this.counter = counter; } public String getName() { return name; } public void setName(String name) { this.name = name; } } static class Saler extends Thread { //明锁 public static ReentrantLock lock=new ReentrantLock(); TicketBox tb; public Saler(String name,TicketBox tb) { super(name); this.tb=tb; } @Override public void run() { while(true) { //找共享对象的锁 lock.lock(); //以下代码段,当线程a 还没有执行完时,不能让b执行 if(tb.getCounter()>0) { tb.setCounter(tb.getCounter()-1); System.out.println(this.getName()+"--卖了一张票,还有.."+tb.getCounter()); }else { break; } lock.unlock(); try { Thread.sleep(40); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
ThreadLocal和线程池
ThreadLocal可以看成是线程的局部变量。
线程A里面set()放的东西只对A线程可见,对其他线程不可见。
package com.part01; public class Demo12 { private static final ThreadLocal<Integer> tl=new ThreadLocal<Integer>(); static class MyThread1 extends Thread { @Override public void run() { tl.set(10); System.out.println(Thread.currentThread().getName()+"......."+tl.get()); } } static class MyThread2 extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName()+"......."+tl.get()); } } public static void main(String[] args) { MyThread1 mt=new MyThread1(); new Thread(mt,"A").start(); MyThread2 mt2=new MyThread2(); new Thread(mt2,"B").start(); } }
线程池
java.util.concurrent包下的Executor接口和ExecutorService接口
java.util.concurrent.Executors类中提供了创建线程池的许多方法,经常使用的有四种。
创建一个带缓存的线程池,动态的分配线程 Executors.newCachedThreadPool()
创建固定大小的线程池 Executors.newFixedThreadPool(10)
创建单任务线程池 Executors.newSingleThreadExecutor()
创建延后或者定期执行的线程 Executors.newScheduledThreadPool(10)
package com.part01; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Demo13 { static class MyThread implements Runnable { @Override public void run() { for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"-------------"+i); } } } public static void main(String[] args) { ExecutorService ex=Executors.newCachedThreadPool();//可变尺寸的线程池 // Executors.newFixedThreadPool(10); //固定大小的线程池 // Executors.newSingleThreadExecutor();//单任务线程池 ex.execute(new MyThread()); ex.execute(new MyThread()); ex.execute(new MyThread()); ex.shutdown(); } }
package com.part01; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class Demo14 { static class Mythread implements Runnable { @Override public void run() { for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"-------------"+i); } } } public static void main(String[] args) { //创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 ScheduledExecutorService pool=Executors.newScheduledThreadPool(5); Mythread mt1=new Mythread(); Mythread mt2=new Mythread(); Mythread mt3=new Mythread(); Mythread mt4=new Mythread(); pool.execute(mt1); pool.execute(mt2); //将t3和t4延迟执行,t3和t4在5s后才执行 pool.schedule(mt3, 5000, TimeUnit.MILLISECONDS); pool.schedule(mt4, 5000, TimeUnit.MILLISECONDS); pool.shutdown(); } }
线程的交互
Wait() 等待,进入阻塞状态,不具备抢cpu资格,同时释放掉锁,释放cpu
Notify()唤醒正在等待的线程