6.多线程

 

进程: 是一个正在执行中的程序;

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

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

线程在控制着进程的执行;

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

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

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

(其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程;)

 

创建线程的第一种方式:    继承Thread,

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

调用线程的start方法(作用:开启线程,调用run方法);

class Demoextends Thread {

//重写run方法;

public void run() {

System.out.println(“demo run”);

}

}

class ThreadDemo{

Public static void main(Stirng[] args) {

//创建一个对象,就是创建一个线程;

Demo d = new Demo()

//调用start方法,开启新线程.执行run方法;

d.start();

}

}

因为多个线程都获得cpu的执行权,cpu执行到谁,谁就运行;

多线程的特性:随机性;

为什么要覆盖run方法?

Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法;也就是说Thread类中的run方法,用于存储线程要运行的代码;

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

getName(): 获取线程名称;线程都有自己默认的名称:  Thread-编号,该编号从0开始;

设置线程名称: setName或者构造函数;

 

创建线程的第二种方式:实现Runnable接口

步骤:     1,定义类实现Runnable接口;

2,覆盖Runnable接口中的run方法;

3,通过Thread类建立线程对象;

4,Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;

5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法;

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

因为:自定义的run方法所属的对象是Runnable接口的子类对象;所以要让线程去执行指定对象的run方法:就必须明确该run方法所属对象;

 

实现方式和继承方式的区别?

实现方式:避免了单继承的局限性;在定义线程时,建议使用实现方式;

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

实现Runnable:线程代码存放在接口的子类的run方法中;

 

注意 创建多线程一定要注意安全问题;

出现问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据的错误;

解决办法:

对多条操作共享数据的语句,只能让一个线程都执行结束,在执行过程中,其他线程不可以参与执行;

同步代码块:

格式:

synchronized(对象) {

需要被同步的代码;

}

同步函数:同步函数使用的锁是this

对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有锁;

同步的前提:

1,必须要有两个或者两个以上的线程;

2,必须是多个线程使用同一个锁;

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

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

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

/*练习:

需求:简单的售票程序;

多个窗口同时售票;

*/

class Ticket implements Runnable {

售票的数量

       privateint ticket = 100;

       Objectobj = new Object();

//重写run方法

       publicvoid run() {

              while(true){

//同步锁

                     synchronized(obj){

//判断售票条件,

                            if(ticket> 0)

                                   System.out.println(Thread.currentThread().getName()+"sale: "+ticket--);

                     }

              }

       }

}

class TicketDemo {

       publicstatic void main(String[] args) {

//创建售票对象;

              Tickett = new Ticket();

//创建线程;把t传入线程中;

              Threadt1 = new Thread(t);

              Threadt2 = new Thread(t);

              Threadt3 = new Thread(t);

              Threadt4 = new Thread(t);

//开启线程;

              t1.start();

              t2.start();

              t3.start();

              t4.start();

       }

}

/*

需求:

银行有一个金库,2个储户分别存300,每次存100,3;

 

目的:改程序是否有安全问题,如果有,如何解决?

 

如何找问题:

1,明确哪些代码时多线程运行代码;

2,明确共享数据

3,明确多线程运行代码中哪些语句是操作共享数据的

 

 

*/

class BankDemo {

       publicstatic void main(String[] args) {

//创建对象;

              Cusc = new Cus();

              //newThread(c).start();

              //newThread(c).start();

//创建线程;

              Threadt1 = new Thread(c);

              Threadt2 = new Thread(c);

              t1.start();

              t2.start();

       }

}

 

class Bank {

       //银行存款总额;

       privateint sum;

       Objectobj = new Object();

       //总额累加;

       publicsynchronized void add(int n) {

              //synchronized(obj){    使用同步代码块实现同步,或者使用同步函数实现同步;

                     sum= sum + n;

                     try{Thread.sleep(10);}catch(Exception e) {}

                     System.out.println("sum="+sum);

              //}

       }

}

//创建存户类;

class Cus implements Runnable {

      

       privateBank b = new Bank();

       publicvoid run() {

              for(intx = 0; x < 3; x++)

//存钱;

                     b.add(100);

       }

      

}

函数需要被对象调用,那么函数都有一个所属对象引用,就是this.所以同步函数使用的锁是this;

如果同步函数被静态修饰,不是this,因为静态中不可以定义this;静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象,类名.class该对象的类型是class;

静态的同步方法,使用的锁是该方法所在类的字节码文件对象 类名.class.

 

//单例设计模式:懒汉式;

存在安全隐患;

使用synchronized锁对象,避免安全隐患;但是每次判断if前都要先执行锁对象,

消耗资源,为了节省资源,synchronized外加上一个if语句进行判断;

class Single {

       privatestatic Single = null;

       privateSingle() {}

       publicstatic Single getInstance() {

              if(s== null) {

                     synchronized(Single.class){

                            if(s== null)

                                   s= new Single();

                     }

              }

              returns;

       }

}

饿汉式:

class Single {

       private static finalSingle s = new Single();

       private Single(){}

       publc static SinglegetInstance() {

              return s;

       }

}

 

死锁:同步中嵌套同步,(了解同步的前提:2个及以上的线程;使用同一个锁);

在编程的过程中尽量不要将同步代码嵌套使用,避免产生死锁.

例:

class TestDemoLock {

//创建静态对象;直接被类名调用;

       static Object o1 = newObject();

       static Object o2 = newObject();

       public static voidmain(String[] args) {

//创建多线程;

              Thread t1 = newThread(new Test());

//开启线程;

              t1.start();

              Thread t2 = newThread(new Demo());

              t2.start();

       }    

}

//实现runnable接口,重写run方法;

class Test implements Runnable {

       public void run() {

              synchronized(TestDemoLock.o1){

                     System.out.println("TestDemoLock.o1>>Test");

                    synchronized(TestDemoLock.o2){

                            System.out.println("TestDemoLock.o2>>Test"); 

                     }    

              }    

       }    

}

class Demo implements Runnable {

       public void run() {

              synchronized(TestDemoLock.o2){

                     System.out.println("TestDemoLock.o2....Demo");

                    synchronized(TestDemoLock.o1){

                            System.out.println("TestDemoLock.o1....Demo");     

                     }    

              }

       }           

}

或者:

class DeadLockDemo {

      

       private static Objectobj1 = new Object();

       private static Objectobj2 = new Object();

      

       public static voidmain(String[] args) {

//使用匿名内部类的方式创建线程并开启;

              new Thread(){

                     publicvoid run() {

                            synchronized(obj1){

                                   System.out.println("第一条线程锁定obj1");

                                   synchronized(obj2){     //要等21行执行结束

                                          System.out.println("第一条线程锁定obj2");

                                   }

                            }    

                     }    

              }.start();

              //使用匿名内部类实现Runnable接口的方式创建线程并开启;

              new Thread(newRunnable() {

                     publicvoid run() {

                            synchronized(obj2){

                                   System.out.println("第二条线程锁定obj2");

                                   synchronized(obj1){     //要等12行执行结束

                                          System.out.println("第二条线程锁定obj1");

                                   }

                            }    

                     }    

              }).start();

       }    

      

}

线程间通信:

1.什么是通信:

在多线程并发执行的时候,可以使用wait()notify()方法在多个线程间互相通信;其实就是多个线程操作同一个资源;但是操作的动作不同;

       2.怎么使用

              通信的代码必须写在同步代码中, 必须使用锁对象来调用wait()notify()

              wait()方法可以控制当前线程等待,直到其他线程调用notify()或者notifyAll()方法才被唤醒

例:开启3条线程,轮流执行打印操作, 执行结果如下:

       /*

       线程1: 1

       线程1: 2

       线程1: 3

      

       线程2: 4

       线程2: 5

       线程2: 6

      

       线程1: 7

       线程1: 8

       线程1: 9

      

       线程2: 10

       线程2: 11

       线程2: 12

      

       线程1: 13

       线程1: 14

       线程1: 15

      

       线程2: 16

       线程2: 17

       线程2: 18

*/

class Test{

      

       public static voidmain(String[] args) {

              final Printer p =new Printer();

              new Thread(){                            //使用匿名内部类创建新线程;

                     publicvoid run() {

                            for(int i = 0; i < 3; i++) {

                                   try{                    

                                          p.print1();

                                   }catch(Exception e) {   //捕获异常

                                          e.printStackTrace();      

                                   }

                            }

                     }    

              }.start();

             

              new Thread(){                            //使用匿名内部类创建新线程; 

                     publicvoid run() {

                            for(int i = 0; i < 3; i++) {

                                  try{

                                         p.print2();     

                                  }catch(Exception e) {

                                         e.printStackTrace();      

                                  }

                            }

                                  

                     }    

              }.start();

              new Thread() {

                     publicvoid run() {

                            for(inti = 0; i<3;i++) {

                                   try{

                                          p.print3();

                                   }

                                   catch(Exceptione) {

                                          e.printStackTrace();      

                                   }

                            }    

                     }

              }.start();

       }    

      

}

 

class Printer {

       private int x = 1;

       private int finy = 1;             //定义标记,用来替换线程;

       public synchronized voidprint1() throws Exception {

              while(finy!=1)                     //如果标记不等于1,就执行wait方法等待;

                     wait();

              System.out.println("线程1: " + x++);

              System.out.println("线程1: " + x++);

              System.out.println("线程1: " + x++);

              System.out.println();

              finy = 2;                       //finy再次赋值;

              notifyAll();                   //唤醒当前类的所有线程;

       }

      

       public synchronized voidprint2() throws Exception {

              while(finy!=2)                     //如果标记不等于2,就执行wait方法等待;

                     wait();

              System.out.println("线程2: " + x++);

              System.out.println("线程2: " + x++);

              System.out.println("线程2: " + x++);

              System.out.println();

              finy = 3;                      

              notifyAll();

       }

       public synchronized voidprint3() throws Exception {

              while( finy !=3)            //如果标记不等于3,就执行wait方法等待;

                     wait();

              System.out.println("线程3: " + x++);

              System.out.println("线程3: " + x++); 

              System.out.println("线程3: " + x++);

              System.out.println();    

              finy = 1;

              notifyAll();    

       }

}

练习:生产者和消费者的练习:

importjava.util.concurrent.locks.*;

 

classProducerConsumerDemo {

       public static void main(String[] args) {

              Resource r = new Resource();

              //开启新线程;

              new Thread(newProducer(r)).start();

              new Thread(newProducer(r)).start();

              new Thread(newConsumer(r)).start();

              new Thread(newConsumer(r)).start();

       }    

}

 

class Resource {

       private String name;

       private int count = 1;

       private boolean flag = false;

       //创建锁;java5.0新特性;

       Lock lock = new ReentrantLock();

 

       Condition c = lock.newCondition();

       Condition c2 = lock.newCondition();

       public void set(String name) throwsException {

              lock.lock();//调用lock方法;获取锁;

              try {

                     while(flag)

                            c.await();  //条件满足则等待;

                     this.name = name +"-_-"+count++;

                     System.out.println(Thread.currentThread().getName()+"........"+"生产者"+this.name);

                     flag = true;

//唤醒c2线程;

                     c2.signal();

              }

//一定要释放锁,所以使用finally

finally {

                     lock.unlock();

              }

       }

       public void out() throws Exception{

              lock.lock();  //调用lock方法;获取锁;

              try {

                     while(!flag)

                            c2.await();  //条件满足则等待;

                     System.out.println(Thread.currentThread().getName()+">>>"+"消费者"+this.name);

                    

                     flag = false;

//唤醒对方线程;

                     c.signal();

              }

//一定要释放锁,所以使用finally

              finally {

                     lock.unlock();

              }    

       }

}

创建生产者类,实现runnable。重写run方法;

class Producerimplements Runnable {

       private Resource s;

       Producer (Resource s) {

              this.s = s;

       }

 

       public void run() {

              while(true) {

                     try {

                            s.set("<商品>");

                     } catch(Exception e) {

                            e.printStackTrace();      

                     }

              }

       }    

}

class Consumerimplements Runnable {

       private Resource s;

Consumer类的构造函数,和Producer类使用同一个Resource对象

       Consumer(Resource s) {

              this.s = s;

       }    

       public void run() {

              while(true) {

                     try {

                            s.out();

                     } catch(Exception e) {

                            e.printStackTrace();      

                     }

              }    

       }

}

 

JDK1.5 中提供了多线程升级解决方案。

将同步Synchronized替换成现实Lock操作。

Object中的waitnotify notifyAll,替换了Condition对象。

该对象可以Lock锁进行获取。

该示例中,实现了本方只唤醒对方操作。

Lock:替代了Synchronized

       lock

       unlock

       newCondition()

Condition:替代了Object wait notify notifyAll

       await();

       signal();

       signalAll();

 

setDaemon守护线程:也叫后台线程;

              设置指定线程为守护线程, 守护线程不会单独运行

 

Join

A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。

join可以用来临时加入线程执行。

例:

class JoinDemo{

       publicstatic void main(String[] args) throws Exception {

              Demod = new Demo();

              Threadt1 = new Thread(d);

              Threadt2 = new Thread(d);

             

              t1.start();

              t1.join();

//当主函数执行到t1.join()时,cpu执行权交给t1,t1线程执行结束,main再执行

              t2.start();

t2.setDaemon(true);   //设置t2为守护线程,程序中只剩守护线程了,程序结束;

              for(intx=0; x< 70; x++)

                     System.out.println(Thread.currentThread().getName()+".>>."+x);

              System.out.println("over");

       }

}

class Demo implements Runnable {

       publicvoid run() {

              for(intx = 0; x < 60; x++)

                     System.out.println(Thread.currentThread().getName()+".."+x);

       }

}

 

setPriority:更改线程优先级:分1 ~10级;

一般使用的级别:Thread.MAX_PRIORITY:线程最高优先级

MIN_PRIORITY     : 线程最低优先级;

NORM_PRIORITY :默认优先级;

yield

publicstatic void yield()

暂停当前正在执行的线程对象,并执行其他线程。也就是交替执行线程;

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值