黑马程序员08_线程
线程
概述:
-
1、进程和线程
进程:
是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。比如在Windows系统中,一个运行的xx.exe就是一个进程。
Java程序的进程里有几个线程:主线程, 垃圾回收线程(后台线程)
线程:
是指进程中的一个执行任务(控制单元),一个进程中可以运行多个线程,多个线程可共享数据。
多进程:
操作系统中同时运行的多个程序。
多线程:
在同一个进程中同时运行的多个任务。一个进程至少有一个线程,为了提高效率,可以在一个进程中开启多个控制单元。 并发运行。如:多线程下载软件。 可以完成同时运行,但是通过程序运行的结果发现,虽然同时运行,但是每一次结果都不一致。 因为多线程存在一个特性:随机性。 造成的原因:CPU在瞬间不断切换去处理各个线程而导致的。 可以理解成多个线程在抢cpu资源。
扩展:
其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
知识点一:自定义一个线程
-
方式1:继承Thread类。
步骤:
1、定义类继承Thread。
2、复写Thread类中的run方法。
目的:将自定义代码存储在run方法。让线程运行。
3、调用线程的start方法,
该方法两个作用:启动线程,调用run方法。
Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。
代码如下:
class myThread extends Thread //1、定义类继承Thread。 { public void run() //2、复写Thread类中的run方法。 { for(int i=0;i<100;i++) { System.out.println("thread::::"+i); } } } class test { public static void main(String[] args) { myThread mt = new myThread(); mt.start(); //3、调用线程的start方法, //mt.run(); //仅仅是调用run方法,单线程 for(int i=0;i<100;i++) { print("main:::"+i); } } public static void print(Object o) { System.out.println(o); } }
-
方式2:实现runnable接口
步骤:
1、定义类实现Runnable接口
2、覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
3、通过Thread类建立线程对象。
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
代码如:
class myThread implements Runnable { public void run()//覆盖run()方法 { for(int i=0;i<100;i++) { System.out.println("mythread::::"+i); } } } class test { public static void main(String[] args) { //调用: myThread mt = new myThread(); Thread mt1 = new Thread(mt); mt1.start(); for(int i=0;i<100;i++) { System.out.println("main::::"+i); } } }
两种方式的区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法。
实现Runnable方式好处:避免了单继承的局限性。在定义线程时,建立使用实现方式。
知识点二:线程名字
class myThread extends Thread { public void run() { for(int i=0;i<100;i++) { System.out.println(this.getName()+"thread::::"+i); } } } class test { public static void main(String[] args) { myThread mt1 = new myThread(); myThread mt2 = new myThread(); mt1.start(); mt2.start(); for(int i=0;i<100;i++) { print("main:::"+i); } } public static void print(Object o) { System.out.println(o); } }
- 练习:模拟买票系统
错误示例:new出了四个窗口卖了400张票
class myThread implements Runnable { private int ticks = 100; public void run() { while(ticks>=0) { System.out.println(Thread.currentThread().getName()+"::::"+ticks--); } } } class test { public static void main(String[] args) { myThread m1 = new myThread(); myThread m2 = new myThread(); myThread m3 = new myThread(); myThread m4 = new myThread(); Thread mt1 = new Thread(m1); Thread mt2 = new Thread(m2); Thread mt3 = new Thread(m3); Thread mt4 = new Thread(m4); mt1.start(); mt2.start(); mt3.start(); mt4.start(); } }
正确示例:四个窗口用一个对象创建线程,就共同拥有一个ticks,因为是一个对象,就只有一个ticks,也就是说只有100张票
myThread m = new myThread(); Thread mt1 = new Thread(m); Thread mt2 = new Thread(m); Thread mt3 = new Thread(m); Thread mt4 = new Thread(m); mt1.start(); mt2.start(); mt3.start(); mt4.start();
知识点四:同步锁
通过分析,发现,打印出0、-1、-2等错票。即多线程的运行出现了安全问题。
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。即对进行相同数据的代码进行同步。
同步的前提:
1、必须要有两个或者两个以上的线程。
2、必须是多个线程使用同一个锁。
同步锁的形式:
-
同步锁形式1:即同步代码块,该锁属于对象 e
synchronized(对象e)
{
需要被同步的代码
}
PS:当对象相同时,才会是实现同步的效果。对象e可以为其他对象,只要报纸一致就行。
代码示例如下:
class myThread implements Runnable { private int ticks = 100; Object obj = new Object(); public void run() { while(true) { synchronized(obj) { if(ticks>0) { System.out.println(Thread.currentThread().getName()+"::::"+ticks--); } } } } }
-
同步锁形式2:同步函数。
public synchronized void 方法名()
函数需要被对象调用,那么函数就有一个所属对象引用,就是this,故同步函数使用的是this锁
代码如下:
public synchronized void add(int n)
{
sum = sum + n;
try{Thread.sleep(100);}catch(Exception e){}
System.out.println("sum="+sum);
}
-
同步锁形式3:静态函数同步锁。因为静态方法进内存时没有对象,只有该类对应的字节码文件对象,即 类名.class,该对像的类型是class
对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
具体代码如下:
class myThread implements Runnable { private int ticks = 100; Object o = new Object(); public void run() { while(true) { synchronized(o)//相当于上锁 { if(ticks>0) { try{Thread.sleep(10);} catch(Exception e){} System.out.println(Thread.currentThread().getName()+"::::"+ticks--); } } } } }