---------------------- android培训、java培训、期待与您交流! ----------------------
多线程(一)
进程:正在执行的程序。每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:进程中的一个控制单元。线程控制着进程的执行。
一个进程至少有一个线程。
Java VM 启动的时候会有一个进程java.exe。
而且这个线程运行的代码存在于main方法中,该线程称之为主线程。
线程创建方式:
方法一:
定义Thread类的子类并重写Thread类的run方法;然后创建该线程(生成该子类的对象)。
调用线程的start方法,该方法有两个作用,启动线程,并调用run方法。
class ThreadDemo { public static void main(String[] args) { MyThread mt = new MyThread();//创建一个线程 mt.start();//启动该线程并运行run方法 mt.run();// 不能直接调用run方法,否则运行多次结果是一样的,相当于普通方法的调用 for(int j=0;j<20;j++) { System.out.println("main :" + j); } } } class MyThread extends Thread { public void run() { for(int i=0;i<20;i++) { System.out.println("run:"+ i); } } }
Thread 类用于描述线程,该类就定义了一个功能,用于存放线程要运行的代码,代码就存放在run方法中。Start方法用于启动线程。
线程都有自己的名称 main方法为主线程 名称是 main 。自定义的线程名称为:Thread-编号 形式 编号从0开始。
也可以通过构造函数创建线程名称,或者setName方法
Thread.currentThread().getName() 可以获取当前线程的名称
class ThreadDemo { public static void main(String[] args) { MyThread mt1 = new MyThread("thread1");//创建一个线程 MyThread mt2 = new MyThread("thread2"); mt1.start();//启动该线程 mt2.start(); for(int j=0;j<50;j++) { System.out.println(Thread.currentThread().getName()+" ..main :" + j); } } } class MyThread extends Thread { //利用构造函数来设置线程的名称,也可以用setName方法 public MyThread(String name) { super(name); } public void run() { for(int i=0;i<50;i++) { System.out.println(Thread.currentThread().getName()+" run:"+ i); } } }
方法二:
1、自定义类实现Runnable接口
2、重写Runnable接口中的run方法
3、建立Thread类的线程对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造方法
5、通过调用Thread类的start方法启动线程并调用Runnable接口中的run方法代码
代码如下:
class TicketDemo { public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t);//创建一个线程 Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); } } class Ticket implements Runnable { private int ticket = 100; public void run() { while(true) { if(ticket>0) { try{ Thread.sleep(5);//线程睡眠 } catch(Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+ " sale:"+ ticket--); } } } }
实现Runnabel接口和继承Thread类的区别:
1、实现的好处是避免了单继承的局限性。
2、线程代码存放的位置不一样:继承存放在子类的run方法中,实现存放在接口子类的run方法中。
通常使用实现Runnable接口。
多线程运行的问题:
当多条语句在操作同一个县城共享数据时,一个线程对多条语句至执行力一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决方法:
对于多条操作共享数据的语句,只能让一个线程都执行完,其他线程不可以参与执行。
Java中可以使用同步代码快
格式如下:
synchronized(对象) {
需要被同步的代码.........
}
此处传递的对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程即使有cpu的执行权,也进不去。
同步的前提:
1、必须要有两个以上的线程才同步
2、多个线程使用同一个锁
同步解决了安全问题但是也消耗了资源
对上述共享数据代码同步得到:
/* 简单的多个窗口同时售票 */ class TicketDemo { public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t);//创建一个线程 Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); } } class Ticket implements Runnable { private int ticket = 200; Object obj = new Object(); public void run() { while(true) { synchronized(obj) {//该对象可以是任意对象 if(ticket>0) { try{ Thread.sleep(5); } catch(Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+ " sale:"+ ticket--); } } } } }
如何找出程序中多线程存在的安全问题:
1、明确哪些代码是多线程运行的代码.(run方法中的代码)
2、明确共享数据(tikcet)
3、明确多线程运行代码中哪些语句是操作共享数据的(对ticket的操作)
也可以对函数进行同步,用synchronized修饰函数即可。
同步函数用的锁是this。
如果同步函数被Static修饰,用的锁是该方法所在类的字节码文件对象,类名.class
---------------------- android培训、java培训、期待与您交流! ----------------------