------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! ------
----------------------------------------线程的概念----------------------------------------
线程的概念:
? 1.我们之前的程序都是"单线程"程序:代码一行一行执行,后面的代码总要等到前面的代码执行完毕,才能获得执行;如果前面的代码很复杂,或者被阻塞,后边的代码就不会被执行得到;
? 2.我们可以将一些代码作为一个"独立"的线程去执行,这样在主程序,将线程启动后,这个线程中的代码就会和主程序的代码"并行"执行。
?
?
要了解"线程"先要了解"进程":
?1.进程:
? ? 1).什么是进程:进程是操作系统的概念,它就是指在操作系统中运行的某个程序;每个程序对于操作系统来说都是一个独立的进程,由操作系统管理,分配内存、CPU执行时间.....
? ? 2).什么是多进程:是指操作系统可以同时管理多个应用程序的同时运行;
? ? 3).多进程的意义:可以充分利用CPU资源;可以使用户有更好的操作体验,可以同时运行多个程序;
?2.线程:
? ? 1).什么是线程:线程是指在一个"主进程"中,可以使某段代码以独立于主进程的方式运行。线程中的代码,与主进程中的代码同时抢占操作系统资源;
? ? 2).什么是多线程:多线程是指,一个主进程可以启动多个线程,去独立运行。
? ? 3).多线程的意义:多线程也可以充分利用CPU资源;可以使我们的几段代码"同时"运行,提高我们的代码效率;
?3.并行和并发:
? ?1).并行:是指多个线程在"某个时间段内"同时运行;
? ?2).并发:是指多个线程在"某个时间点上"同时的访问同一资源;
?
----------------------------------------多线程的实现方式----------------------------------------
多线程程序实现的方式1:
?1.Java中一个线程使用"Thread"类表示;
?2.实现线程的方式:
? ? 1).定义一个线程类,继承自Thread;
? ? 2).重写run()方法;将要在此线程中执行的代码写到这里;
? ? 3).启动线程:
? ? ? ? a.实例化一个自定义线程类的对象;
? ? ? ? b.调用对象的start()方法启动线程;
?3.注意:
? ?1).启动线程,一定要使用start()方法。调用run()不会产生编译错误,但这只是简单的方法调用,不是启动线程;
? ?2).不能多次的调用start()方法;??
public class MyThread extends Thread {//自定义一个线程 @Override public void run() {//重写run方法 for (int i = 0; i < 100; i++) { System.out.println("i = " + i); } } }
?
?
?
public class Demo { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); // t.run();//这不是启动线程,这只是简单的方法调用; for(int k = 0;k < 100 ; k++){ System.out.println("k = " + k); } }
?
?
多线程程序实现的方式2:
? 1.自定义实现Runnable接口;
? 2.重写run()方法;
? 3.启动线程:
? ? ? 1).实例化我们自定义类的对象;
? ? ? 2).实例化一个Thread对象,将我们的自定义对象作为参数传递给Thread的构造方法;
? ? ? 3).调用Thread对象的start()方法启动线程
public class MyRunnable implements Runnable {// 实现Runnable接口自定义线程 @Override public void run() {// 复写run方法 for (int i = 0; i < 100; i++) { System.out.println("i = " + i); } } }
?
public class Demo { public static void main(String[] args) { MyRunnable myRun = new MyRunnable(); Thread t = new Thread(myRun); t.start();// 启动县城关 for (int k = 0; k < 100; k++) { System.out.println("k = " + k); } } }
?JDK5实现线程的方式3:
? 1.自定义类,实现Callable接口;
? 2.重写:call()方法;
? 3.使用"线程池"的方式去启动线程;
public class MyCallable implements Callable{ @Override public Object call() throws Exception { for(int i = 0; i < 100 ; i++){ System.out.println("i = " + i); } return null; } }
?
public class Demo { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(2);//线程池 MyCallable myCall = new MyCallable(); service.submit(myCall);//使用线程池的方法启动线程 service.shutdown(); for(int j = 0;j < 100; j++){ System.out.println("j = " + j); } } }
---------------------------------------------多线程的方法-------------------------------------------------------------
获取和设置线程对象名称:
? 1.一个线程类,可以实例化多个对象,每个对象都可以以单独的线程去执行;
? 2.每个线程,都有一个默认的名称,格式:"Thread-索引":
? 3.设置线程名称:setName(String name):
? ? ?获取线程名称:getName();
public class Demo { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.setName("章子怡");//设置线程名称 t2.setName("汪峰");//设置线程名称 t3.setName("撒贝宁");//设置线程名称 t1.start(); t2.start(); t3.start(); } }
?线程优先级:
? 1.Java中的线程优先级:从低到高:1--10
? 2.默认的优先级是:5
? 3.设置优先级:
setPriority(int p):设置优先级:一定要在1--10的范围内,否则抛出异常;
getPriority():获取线程的优先级;
? 4.注意:
? ? ?1).较高的优先级,只代表有机会先被执行,但仍由操作系统管理,仍然有很多的不确定性;
? ? ?2).所以,大家不要利用"优先级"的技术,去试图向让某个线程先执行完毕;
? ? ?3).如果线程内,有较少的代码,执行逻辑很简单,那么优先级的效果就不会明显;
public class Demo { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); MyThread t4 = new MyThread(); MyThread t5 = new MyThread(); MyThread t6 = new MyThread(); //设置优先级 // t1.setPriority(100);//一定要在1--10的范围内,否则抛出异常; t1.setPriority(1); t2.setPriority(10);//最高优先级 t3.setPriority(1); t4.setPriority(1); t5.setPriority(1); t6.setPriority(1); //设置线程名称 t1.setName("线程1"); t2.setName("线程2"); t3.setName("线程3"); t4.setName("线程4"); t5.setName("线程5"); t6.setName("线程6"); //启动线程 t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); } }
?
?
线程的休眠:
? 1.public static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
?
public class MyThread extends Thread{ @Override public void run() { for(int i = 0; i < 10 ;i++){ Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String str = sdf.format(date); System.out.println(str); //休息一秒 try { Thread.sleep(1000);//休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }
?线程的礼让:
? 1.public static void yield():暂停当前正在执行的线程对象,并执行其他线程。(退回到"就绪"状态)
? 注意:礼让后,很可能会被操作系统再次分配执行,所以,不能利用这个技术试图让某个线程最后执行完毕
public class MyThread extends Thread{ public void run() { for(int i = 0;i < 1000 ; i++){ if(this.getName().equals("汪峰")){ Thread.yield();//礼让:退回到"就绪"状态,有可能会被操作系统再次分配执行; } System.out.println(this.getName() + " i = " + i); } }; }
守护线程:
? 1.public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。
? 2.默认情况下,当主进程开出一个线程后,都会等待线程结束后,主进程才会结束;这个就是非守护线程;
? 3.我们可以将线程设为"守护线程":当主进程完毕时,开出的所有守护线程也跟着结束;但是不会立即结束,会有个小缓冲;
public class Demo { public static void main(String[] args) { MyThread t1 = new MyThread(); t1.setName("小兵"); //将线程设为守护线程 t1.setDaemon(true); t1.start(); for(int i = 0; i < 100 ;i++){ System.out.println("将军正在杀敌...i = " + i); } System.out.println("将军杀敌完毕,打道回府!!"); } }
?线程的中断:
? public final void stop():
? public void interrupt():基于在线程的内部,当处于以下三种阻塞状态时,才可以促使其停止:
? ?1.Object--wait():
? ?2.Thread--join():
? ?3.Thread--sleep():
public class MyThread extends Thread { @Override public void run() { for(int i = 0;i < 10 ; i++){ System.out.println("我不停:i = " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("线程出现异常了,可能是外部试图停掉我,那么好吧,拜拜......"); return; } } } }
?
public class Demo { public static void main(String[] args) { MyThread t1 = new MyThread(); t1.start(); // 主进程等待3秒钟,如果线程不结束,这里给它干掉 System.out.println("主进程等待3秒..."); try { Thread.sleep(1000 * 3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主进程醒来,干掉线程......"); // t1.stop();//过时,不用 // 线程中断,使其抛出异常 t1.interrupt(); } }
?使用同步解决并发访问的问题:
? 1.在共享资源上(一般是一些方法)使用关键字:synchronized
? 2.作用:当一个线程访问时,其它线程全部列队等待;这种机制保证了这个方法在同一时刻只能被一个线程访问;
? 3.线程同步的方式:
? ? ?1).同步代码块:
synchronized语法:
?
? synchronized(被锁的对象){
? //同步代码
? }
?注:被锁的对象:当一个线程访问此段代码时,会将这个对象中所有的"同步代码块"和"同步方法"加锁,
? ? ?也就意味着,一个线程访问一段同步代码块,其它线程不能访问"被锁对象"的其它"同步代码块"和"同步方法";
?
? ? ?2).同步方法:
? ? ?可以在方法声明时,添加关键字:synchronized,表示这个方法内部的所有代码都是同步的。
?
线程组:
?1.我们可以将多个线程,进行分组管理;好处:可以对组内的线程进行统一操作;
?2.所有的线程,默认都属于"主线程组";
?3.我们可以为线程进行分组;
public class MyThread extends Thread { public MyThread() { System.out.println("实例化一个我的对象,很耗时哟,需要5秒钟......"); for (int i = 0; i < 5; i++) { System.out.println(i + 1); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void run() { System.out.println("线程打印:run()......"); } }
?
public class Demo { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); ThreadGroup grp1 = t1.getThreadGroup(); ThreadGroup grp2 = t2.getThreadGroup(); System.out.println("线程1的所在的组:" + grp1.getName()); System.out.println("线程2的所在的组:" + grp2.getName()); // 为线程分组 MyThread t3 = new MyThread(); MyThread t4 = new MyThread(); // 实例化一个"线程组"对象 ThreadGroup group = new ThreadGroup("我的第一小队"); // 跟线程组关联 Thread th1 = new Thread(group, t3); Thread th2 = new Thread(group, t4); th1.start(); th2.start(); // 主进程睡眠3秒钟 try { System.out.println("主进程睡眠3秒......"); Thread.sleep(1000 * 3); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("主进程醒来,一次停止组内所有线程......"); // 可以一次停止组内的所有线程 group.interrupt(); } }
?线程池:
1.当使用继承Thread的方式实现线程,如果想反复的使用此线程,不能多次调用start();可以多次实例化此类的对象,然后再启动线程。
2.如果线程的启动,或者构造比较耗时,那么就大大的影响效率;
3.JDK5之后,提供了一个"线程池",这个"线程池"可以缓存一些"线程对象",如果需要再次使用时,无需重复构造,直接从"池"中取出线程对象使用即可;
?4.JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
? ? ? ?public static ExecutorService newCachedThreadPool():创建一个可根据需要创建新线程的线程池
? ? ? ?public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池 ? ? ? ?public static ExecutorService newSingleThreadExecutor():创建一个使用单个worker线程的ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法:
? ? Future<?> submit(Runnable task)
? ? <T> Future<T> submit(Callable<T> task)
?
?
1.获取一个线程池对象:
ExecutorService service = Executors.newFixedThreadPool(2);
2.调用线程池的submit()方法执行线程:
3.接收返回值:Future<Integer> result = service.submit(new MyCallable());
public class Demo { public static void main(String[] args) { /* * MyThread t1 = new MyThread();//需要5秒 t1.start(); * * System.out.println("主进程等待2秒......"); try { Thread.sleep(1000 * 2); } * catch (InterruptedException e) { e.printStackTrace(); } * * System.out.println("再次启动MyThread"); t1 = new MyThread();//再次需要5秒 * t1.start(); */ // 获取一个"线程池"对象 ExecutorService service = Executors.newFixedThreadPool(2); MyThread t3 = new MyThread();// 需要5秒 // 执行线程t3 service.submit(t3); System.out.println("主进程休息2秒钟......"); try { Thread.sleep(1000 * 2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主进程再次启动线程......"); service.submit(t3);// 无需再次构造 // 将线程池停止 service.shutdown(); } }
?
public class MyThread extends Thread { public MyThread() { System.out.println("实例化一个我的对象,很耗时哟,需要5秒钟......"); for (int i = 0; i < 5; i++) { System.out.println(i + 1); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void run() { System.out.println("线程打印:run()......"); } }
?匿名内部类的方式实现线程:
1.new Thread(){Thread匿名子类};
2.new Thread(new Runnable(){}){};
3.new Thread(new Runnable(){}){Thread匿名子类};
public class Demo { public static void main(String[] args) { new Thread() { // 重写run()方法 @Override public void run() { System.out.println("a"); } }.start(); new Thread(new Runnable() { @Override public void run() { System.out.println("b"); } }) { }.start(); new Thread(new Runnable() { @Override public void run() { System.out.println("c"); } }) { // 重写run()方法 @Override public void run() { System.out.println("d"); } }.start(); } }
?
原文:http://shuangxi-zhu.iteye.com/blog/2247071