多线程
-
进程和线程的概述
Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
线程生命周期,借用菜鸟教程的图一下
-
线程实现
线程有两种实现方式,一种是继承Thread类实现,另外一种是实现Runnable接口实现,两种线程的实现方式,都是要实现run方法做为执行体的,以下分别记录两种线程实现方法的具体操作。
- 继承Thread类实现
1.继承Thread类,并重写run方法
1 public class MyThread extends Thread{ 2 3 @Override 4 public void run() { 5 //执行体 6 } 7 }
2.给MyThread类添加name属性和有参数构造方法,方便后面定义线程是命名(在这只是为了方便看)
1 public class MyThread extends Thread{ 2 3 private String name; 4 5 public MyThread(String name) { 6 super(); 7 this.name = name; 8 } 9 10 @Override 11 public void run() { 12 13 } 14 }
3.在线程体中添加要执行的代码部分,这里一输出一句内容为执行体作为实验,为了放慢速度来观察,让线程隔一秒休眠一次再运行
1 @Override 2 public void run() { 3 4 System.out.println("我是线程"+name); 5 6 try { 7 Thread.sleep(1000);//休眠一秒 8 } catch (InterruptedException e) { 9 e.printStackTrace(); 10 } 11 }
4.创建一个测试类,添加主方法来实例化线程和运行
1 public class TestMyThread { 2 3 public static void main(String[] args) { 4 5 MyThread thread1 = new MyThread("线程A"); 6 MyThread thread2 = new MyThread("线程B"); 7 8 thread1.start();//执行线程 9 thread2.start(); 10 } 11 }
执行效果如下:
- 实现Runnable接口,和上面一样,需要重写run方法,不同点在于实例化方式,实现Runnable接口的一个例子如下,通过在代码中注释,这里一步一步写了
-
线程睡眠
sleep()是静态方法,所以最好的调用方法就是 Thread.sleep()。作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。
sleep()与wait()的比较
wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。而sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”。但是,wait()会释放对象的同步锁,而sleep()则不会释放锁。
-
线程同步synchronized
1.为了下面个同步的研究,首先创建一个Entry类,该类的作用是从0开始打印到9,表明线程的执行次数
1 class Entry{ 2 3 public void show(String name) {//后面传入线程名 4 5 for(int i = 0; i < 10; i++) { 6 7 try { 8 9 Thread.sleep(1000); 10 System.out.println(name+"----"+i);//线程名+执行序数i 11 12 } catch (InterruptedException e) { 13 14 e.printStackTrace(); 15 } 16 } 17 18 } 19 }
2.创建线程执行体类
1 public class MyThread extends Thread{ 2 3 private Entry entry; 4 private String name; 5 6 public MyThread(Entry entry, String name) { 7 this.entry = entry; 8 this.name = name; 9 } 10 11 @Override 12 public void run() { 13 14 entry.show(name); 15 } 16 }
3.创建测试类
1 public class Test { 2 3 public static void main(String[] args) { 4 Entry entry = new Entry(); 5 6 MyThread mt1 = new MyThread(entry, "线程1"); 7 MyThread mt2 = new MyThread(entry, "线程2"); 8 9 mt1.start(); 10 mt2.start(); 11 } 12 }
添加同步之前的执行效果:
看得出来,线程1和线程2是随机被执行的,这样不是我们想要的效果,那么需要同步。
4.所有的同步,在Entry类做修改,分别如下
同步实例方法
在该方法上加synchronized,如下
1 class Entry{ 2 3 //线程同步成员方法,只对同一个对象调用次方法时有效,如果不是同一个对象,不行 4 //线程同步成员方法 5 public synchronized void show(String name) { 6 7 for(int i = 0; i < 10; i++) { 8 9 try { 10 11 Thread.sleep(1000); 12 System.out.println(name+"----"+i); 13 14 } catch (InterruptedException e) { 15 16 e.printStackTrace(); 17 } 18 } 19 } 20 }
同步后的执行效果如下
线程1一旦先获得资源执行,那么线程2只能等线程1执行完了才能开始。
同步类方法
将Entry类种的方法改为静态方法,并添加同步(在该方法上加synchronized),如下:
1 class Entry{ 2 //类方法 3 //线程同步类方法,无论使用同一个对象还时不同对象来调用次方法,同步都有效 4 public synchronized static void show(String name) { 5 6 for(int i = 0; i < 10; i++) { 7 8 try { 9 10 Thread.sleep(1000); 11 System.out.println(name+"----"+i); 12 13 } catch (InterruptedException e) { 14 15 e.printStackTrace(); 16 } 17 } 18 } 19 }
同步代码块
class Entry{ public void show(String name) { //this,调用方法的对象 synchronized (this) {//线程同步代码块 for(int i = 0; i < 10; i++) { try { Thread.sleep(1000); System.out.println(name+"----"+i); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
执行效果