多线程

来源:互联网 发布:学车网络招生平台 编辑:程序博客网 时间:2024/06/07 01:14
进程:一个正在执行的程序

            每一个进程执行都有一个执行程序,该顺序是一个执行路径,或者叫一个控制单元

线程:进程中的一个独立控制单元.

            线程在控制着进程的执行

            一个进程中至少有一个线程

JAVA VM 启动的时候会有一个进程 java.exe

该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于 main 方法中

该线程称之为主线程,主线程执行 main()方法中的代码。

     多线程 (举例:迅雷下载)

扩展:JVM启动不止一个线程,还有负责垃圾回收机制的线程


如何在自定义代码中,自定义一个线程??  (Thread 类)

创建新线程第一种方式:继承Thread 类

步骤:

1、定义类继承Thread

2、重写Thread类的run()方法;

    目的:将自定义的代码存储在run()方法中,让线程运行。

3、调用线程 start()方法:该方法两个作用:启动线程;调用run()方法;

 

每次运行结果都不同,因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。

   某一个时刻,只能有一个程序在运行。(多核除外,多核的瓶颈为内存大小)

CPU在做着快速的切换,以达到看上去是同时运行的效果。

可以形象的把多线程的运行行为看做是互相抢夺CPU的执行权。

多线程特性;随机性。谁抢到谁执行,至于执行多长,CPU说的算。

 

为什么要覆盖 run() 方法?

Thread 类用于描述线程,该类定义了一个功能,用于存储线程要运行的代码,该存储功能的就是

run() 方法 ,也就是说Thread 类中的run()方法,用于存储线程要运行的代码。


public class ThreadDemo {public static void main(String[] args) {Demo d = new Demo();    //创建一个线程d.start();               //start()方法,启动线程并执行该线程的run()方法        d.run();                //仅仅是对象调用,线程创建了,但是并没有运行for(int i = 0; i < 50; i++)System.out.println("Hello World--" + i);}}class Demo extends Thread {public void run() {for(int i = 0; i < 50; i++)System.out.println("Demo Run --" + i);}}
线程的运行状态:

获取线程对象及名称:

线程都有自己的名称,Thread-编号为线程的默认名称,通过调用 getName()获得;

Static ThreadcurrentThread() :获取当前线程对象;

getName():获取线程名称;

设置线程名称:setName() 或者使用super()调用父类构造函数;

 

创建线程的第二种方式:实习Runable接口

步骤:

1、  定义类实现Runable接口

2、  覆盖Runable接口中的run()方法

        将线程要运行的代码存放在run()方法中。

3、  通过Thread类建立线程对象

4、  Runable接口的子类对象作为实际参数传递给Tread类的构造函数

   为什么要将Runable接口的子类对象传递给Thread的构造函数。

   因为自定义的run()方法所属的对象是Runable接口的子类对象

   所以要让线程去调用指定对象的run()方法,就必须明确该run()方法所属对象。

5、  调用Thread类的start()方法开启并调用Ruanable接口子类的run()方法;

 

实现方式和继承方式有什么区别?

实现方式好处:避免了单继承的局限性。

在定义线程时,建议使用实现方式;

 

两种方式区别:

继承Thread :线程代码存放在Thread子类run()方法;

实现Runable,线程代码存放在接口的子类run()方法中。

//多个窗口同时售票class Ticket implements Runable               //定义类实现Runable 接口{private int tick = 100;public void run()             ///重写run()方法{while(true){if(tick > 0){System.out.println(Thread.currentThread().getName() + "sale```" + tick--);}}}}public class TicketDemo {public static void main(String[] args) {Ticet t = new Ticket();Thread t1 = new Thread(t);    //通过Thread 类建立线程对象Thread t2 = new Thread(t);  Thread t3 = new Thread(t);Thread t4 = new Thread(t);t1.start();         //通过Thread类的star()方法开启线程,并调用Runable接口子类的run()方法t2.start();t3.start();t4.start();}}

多线程运行中的安全问题:

问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完

另一个线程参与进来执行,导致共享数据的错误。

   解决办法:对多条操作共享数据的语句,只能让一个线程执行完毕,在执行过程中,其他线程不可以参与执行。


Java对于多线程的安全问题提供的专业解决方式:同步代码块。

Synchronized(对象)

{

   需要被同步的代码

}

对象如同锁,持有锁的线程可以在同步中执行,

没有持有锁的线程即使获取了CPU的执行权也进不去,因为没有获取锁。


同步的前提:

1、  必须要有两个或者两个以上的线程。

2、  必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题;

弊端:多个线程都需要判断锁,较为消耗资源。

class Ticket implements Runnable{private int tick = 100;          Object obj = new Object()public void run() {while(true){synchronized(obj){if(tick > 0){try{Thread.sleep(10);}catch(InterruptedException e) {e.printStackTrace();}     System.out.println(Thread.currentThread().getName() + "-----sale-----" + tick--);}}}}}

/*需求分析:银行有一金库  有2个储户同时往里面存300,每次存100,分3次*/ 如何找问题:   1、明确哪些代码是多线程运行代码;  2、明确共享数据;  3、明确多线程运行代码中哪些语句是操作共享数据的;class Bank {public int sum;Object obj = new Object();public void add(int n) {synchronized(obj){sum = sum + n;try{Thread.sleep(20);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"--sum = " + sum);}}}class Customer implements Runnable{private Bank b = new Bank();public void run(){for(int i=0; i<8; i++){b.add(100);}}}public class BankDemo {public static void main(String[] args){Customer c = new Customer();Thread t1 = new Thread(c);Thread t2 = new Thread(c);t1.start();t2.start();}}

同步函数用的是哪一个锁?同步函数需要被对象调用,那么函数都有一个所属对象引用,就是this所以同步函数使用的锁是thisclass Ticket implements Runnable{private int tick = 100;  Object obj = new Object();  boolean flag = true;public void run() {if(flag){while(true){synchronized(this){if(tick > 0){try{Thread.sleep(100);}catch(InterruptedException e) {e.printStackTrace();}     System.out.println(Thread.currentThread().getName() + "-----code-----" + tick--);}}}}elsewhile(true){show();}}public synchronized void show(){if(tick > 0){try{Thread.sleep(10);}catch(InterruptedException e) {e.printStackTrace();}     System.out.println(Thread.currentThread().getName() + "-----show-----" + tick--);}}}
如果同步函数被静态修饰后,使用的锁是什么?静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码对象,类名.class,该对象的类型是class.静态的同步方法,使用的锁是该方法所在类的字节码文件对象,即 类名.classclass Ticket implements Runnable{private int tick = 100;  Object obj = new Object();  boolean flag = true;public void run() {if(flag){while(true){synchronized(Ticket.class){if(tick > 0){try{Thread.sleep(100);}catch(InterruptedException e) {e.printStackTrace();}     System.out.println(Thread.currentThread().getName() + "-----code-----" + tick--);}}}}elsewhile(true){show();}}public static synchronized void show(){if(tick > 0){try{Thread.sleep(10);}catch(InterruptedException e) {e.printStackTrace();}     System.out.println(Thread.currentThread().getName() + "-----show-----" + tick--);}}}

/*单例设计模式*///饿汉式class Single{private static final Single s = new Single();private Single() {}pbulic static Single getInstance(){return s;}}//懒汉式class Single{private static Single s = null;private Single() {}public static Single getInstance(){if(s==null){synchronized(Single.class){if(s==null){s = new Single();}}}return s;}}

面试:懒汉式和饿汉式有什么不同?

懒汉式的特点用于实例的延迟加载;

懒汉式延迟加载的问题:如果多线程访问时会出现安全问题,可以用加同步

来解决,而加同步的方式用同步代码块和同步函数都可以,但是稍微有些

低效,用双重否定的形式可以解决效率问题,加同步时使用的锁为该类所属的

字节码对象,即 类名.class

/*死锁:同步中嵌套同步,而锁却不同*/class Test implements Runnable{private boolean flag = true; Test(boolean _flag){this.flag = _flag;}public void run(){if(flag){while(true){synchronized(MyLock.locka){System.out.println("if locka");synchronized(MyLock.lockb){System.out.println("if lockb");}}}}else{while(true){synchronized(MyLock.lockb){System.out.println("else lockb");synchronized(MyLock.locka){System.out.println("else locka");}}}}}}class MyLock{static Object locka = new Object();static Object lockb = new Object();}public class DeadLockDemo{public static void main(String[] args){Thread t1 = new Thread(new Test(true));Thread t2 = new Thread(new Test(false));t1.start();t2.start();}}