多线程 概述
1.进程:是一个正在执行中的程序。
每个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元(线程)。
2.线程:就是进程中的一个独立的控制单元。
线程控制着进程的执行,一个进程中,至少有一个或一个以上的线程。
创建线程的第一种方式:继承Thread类。
步骤:
1.定义类继承Thread。
2.复写Thread类中的run方法(目的:将自定义代码存储在run方法。让线程运行)
3.调用线程的Start方法,该方法两个作用:启动线程,调用run方法(直接调用run方法也是可以的,但是那样就不是多线程了)
1 class ThreadTest extends Thread{ 2 3 4 5 public void run(){ 6 7 8 9 线程运行代码; 10 11 } 12 } 13 14 class Test{ 15 16 public static void main(String[] args){ 17 18 19 ThreadTest threadTest = new ThreadTest(); 20 21 thread.start(); 22 23 } 24 25 } 26 27 28 29
也可以使用匿名内部类的方式:
1 class Test{ 2 3 public static void main(String[] args){ 4 5 new Thread(){ 6 7 public void run(){ 8 9 线程运行代码; 10 11 } 12 13 }.start(); 14 15 } 16 17 }
创建线程的第二种方式:实现Runnable接口。
实现步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法,将线程要运行的代码存放在该run方法中。
3.通过Thread类建立线程对象。
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
5.调用Thread类的start方法开启线程并调用Runnable接口子类的的run方法。
1 class RunnableTest implements Runnable{ 2 3 public void run(){ 4 5 6 线程要运行代码; 7 8 } 9 10 } 11 12 class TestDemo{ 13 14 public static void main(String[] args){ 15 16 RunnableTest runnableTest = new RunnableTest(); 17 Thread thread = new Thread(runnableTest); 18 thread.start(); 19 /* 20 简化到一步 new Thread(new Runnable()).start(); 21 22 */ 23 24 } 25 26 27 }
使用匿名内部类:
1 class TestDemo{ 2 3 public static void main(String[] args){ 4 5 new Thread(new Runnable(){ 6 7 public void run(){ 8 9 线程要运行代码; 10 11 } 12 }).start(); 13 14 } 15 16 }
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子接口对象。 所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
实现方式和继承方式有什么区别:
(1)、实现方式好处:避免了单继承的局限性。
(2)、在定义线程时:建议使用实现方式
(3)、两种方式区别:
1.继承Thread:线程代码存放在 Thread 子类 run 方法中(单继承)
2.实现Runnable:线程代码存放在接口的子类的 run 方法中(建议使用这种方式,可以实现多继承接口)
多线程好处:
原本我们如果是单线程的情况下,我们定义在死循环就一直运行死循环的代码,不会运行其他的代码。 多线程的好处就体现出来了,他可以让我们的代码实现同步运行。
发现运行结果每一次都不同。
因为多个线程都获取CPU的执行权。CPU执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个序在运行。(多核除外)。
CPU在做着快速的切换,以达到看上去是同时运行的效果。
线程(Thread)类中已经封装好的方法:
static Thread currentThread():获取当前线程对象。
getName(): 获取线程名称。 设置线程名称:setName或者构造函数。(线程都有自己默认的名字,Thread-编号 该编号从0开始。 )
注意:主线程不会设置名称,它的线程名是默认的(main);
线程间的通讯通信
线程间通信-等待唤醒机制
wait(),notify(),notifyAll(),都使用在同步中,因为要对持有监视器(锁)的线程操作。
这些方法在操作同步中,线程同步,都必须要表示它们所操作线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁notify唤醒,不可以对不同锁中的线程进行等待唤醒。 也就是说,等待和唤醒必须是同一个锁。所以要使用在同步中,因为只有同步才具有锁。
wait() sleep()的区别
wait() :释放资源,释放锁 。
sleep() :释放资源,不释放锁。
其实就是多个线程在操作同一个但是操作的动作不同(一个是wait 一个是notify)。
同步:
对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获取CPU的执行权,也进不去,因为没有获取锁。
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源。
package com.lovo; /** * 同步代码块 * @author Mine * */ public class Sync implements Runnable{ private int value; public void run(){ for(int count=0;count<3;count++){ synchronized(this){//当使用对象锁时,其他线程是无法使用这个块的代码 ,所以value的值是连续的 value++; try { Thread.sleep(1000);//线程休眠 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+":value="+value);//打印value的值 } } } package com.lovo; public class SyncTest { public static void main(String[] args){ Sync s=new Sync(); new Thread(s).start(); new Thread(s).start(); } }
package com.lovo; /** * 同步方法 * @author Mine * */ public class Syncc implements Runnable { private int value; public void run(){ printValue(); } public synchronized void printValue(){ for(int count=0;count<3;count++){ value++; try { Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName()+"value="+value); } } } package com.lovo; public class SynccTest { public static void main(String[] args){ Syncc s=new Syncc(); new Thread(s).start(); new Thread(s).start(); } }