Croe Java(九)

 
知识点:线程
什么是进程?进程就是一个一个独立的应用程序,它们有独自的资源(变量,内存等),进程是数据独占的
什么是线程?线程是多个线程共享一块资源(变量),数据是共享的,那么这样的话,容易出现问题,比如卖 2 个线程卖 100 张票,可能出现卖了大于 100 章票的情况,超出了临界资源,这样的话就不合理了,我们可以通过同步来处理这种数据并发,它依附于进程,是轻量级的进程
如何操作一个线程呢?通过 Thread 类的对象,这种对象叫做代理对象,通过这个对象可以操作线程,凡是访问 JVM 的外部资源的时候,都需要通过代理对象的方式访问
 
线程实现的两种方式:
1 .继承 Thread
2 .实例化该类或者是 Thread 类的对象
3 .对象 .Start()
 
1 .实现 Runnable 接口
2 .实例该类对象
3 .实例 Thread 对象,并把上面的实例当做参数传递给 Thread 构造器
两种方式的区别: Runnable 的线程可以实现多继承,并可以很方便的共享数据,一个 Runnable 的实例可以构造出多个线程,相同点是都必须通过 Thread 来建立线程
例如:
继承 java.lang.Thread
class MyThread extends Thread{
   public void run(){
   需要进行执行的代码,如循环。
       }
}
启动线程
public class TestThread{
 public static void main(){
     Thread t1=new Mythread();
        T1.start();
 }
}
实现 java.lang.Runnable 接口:
Class MyThread implements Runnable{
 Public void run(){
             
 }
}
这种实现可以再继承其他类。
启动线程时不同前者
public static void main(){
       Runnable myThread = new MyThread();
       Thread t = new Thread(myThread);
       t.start();
}
当调用 start 方法时, JVM 会到 OS 中产生一个线程。但是不一定马上运行该线程,只是准备就绪状态
/**
 * 知识点:
 * 线程的两种创建使用方法
 * man1 类:采用第一继承 Thread 的方式
 * man2 类:采用实现 Runnable 接口方式
 * man3 类:继承 Thread 同时实现 Runnable 接口
 */
package MY.module09.tarena.two;
 
public class Man {
      
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
             
//          man1 m=new man1();// 第一种方法启动线程
//          m.start();
//          man2 m=new man2();// 第二种方法
//          Thread t=new Thread(m);
//          t.start();
              man3 m=new man3();// 可以同时继承和实现
              m.start();
              while(true){
                     System.out.println("wan youxi......");
              }
       }
 
}
class man1 extends Thread{
       public void run(){
              while(true){
                     System.out.println("work........");
              }
       }
}
 
class man2 implements Runnable{
 
       public void run() {
              // TODO Auto-generated method stub
              while(true){
                     System.out.println("work........");
              }
       }
}
class man3 extends Thread implements Runnable{
       public void run() {
              // TODO Auto-generated method stub
              while(true){
                     System.out.println("work........");
              }
       }
}
 
Thread.currentThread().getName()
/**
 * 知识点:
 * 得到线程的名字
 * 程序目标:
 * 创造几个线程,在显示的时候能够看到线程的名字
 */
package MY.module09.tarena.currentThread;
 
public class People {
 
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              P p1=new P();
              P p2=new P();
              p1.start();
              p2.start();
              String name=Thread.currentThread().getName();
              while(true){
                     System.out.println(name+" xue xi");
              }
       }
 
}
class P extends Thread{
       public void run(){
              String name=Thread.currentThread().getName();
              while(true){
                     System.out.println(name+" wan you xi.....");
              }
       }
}
 
看一个问题:有 100 张票,需要两个售票口去卖这 100 张票,我们写一个线程的程序来模拟这个操作
/**
 * 发现问题:
 * 程序目标:
 * 造两个线程,让他们同时去卖 100 张票,可是最后卖了 200 张票,为什么呢
 * 因为实例了两个对象
 * 这样他们操作的不是同一个资源了(变量),没有数据并发
 */
package MY.module09.tarena.synchronizdes;
 
public class TestSaleTickets {
 
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              // 使用继承 Thread 方式
              // 我们会发现卖了 200 张票,为什么呢?因为实例了两个对象
              // 这样他们操作的不是同一个资源了(变量),没有数据并发
              SaleTicket st=new SaleTicket();
              SaleTicket st2=new SaleTicket();
              st.start();
              st2.start();
              // 使用实现 Runnable 接口方式
       }
 
}
class SaleTicket extends Thread{
       private int Ticket=1;
       public void run() {
              // TODO Auto-generated method stub
              String name=Thread.currentThread().getName();
              while(true){
                     if(Ticket<=100){// 只有 100 张票,这些资源是被线程共享的
                            System.out.println(name+": 销售了第 "+Ticket+++" 张票 ");
                     }
              }
       }
      
}
// 这种方法可以数据并发,使用同一个资源,但是存在不安全问题
class SaleTicket2 implements Runnable{
       private int Ticket=1;
       public void run() {
              // TODO Auto-generated method stub
              String name=Thread.currentThread().getName();
              while(true){
                     if(Ticket<=100){// 只有 100 张票,这些资源是被线程共享的
                            System.out.println(name+": 销售了第 "+Ticket+++" 张票 ");
                     }
              }
       }
      
}
/**
 * 发现问题:
 * 通过使用 Runnable 方式,看似解决了使用同一资源的问题,但是里面
 * 很不安全,例如:我们可以让线程休眠一下,会发现什么问题呢?
 */
package MY.module09.tarena.synchronizdes;
 
public class TestSaleTickets2 {
 
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              SaleTicket3 st=new SaleTicket3();
              Thread t1=new Thread(st);
              Thread t2=new Thread(st);
              t1.start();
              t2.start();
       }
 
}
class SaleTicket3 implements Runnable{
       private int Ticket=1;
       public void run() {
              // TODO Auto-generated method stub
              String name=Thread.currentThread().getName();
              while(true){
                     if(Ticket<=100){// 只有 100 张票,这些资源是被线程共享的
                            try {
                                   Thread.sleep(1);
                            } catch (InterruptedException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                            }
                            System.out.println(name+": 销售了第 "+Ticket+++" 张票 ");
                     }
              }
       }
      
}
上面那个程序总共销售了 101 张票,因为当有一个线程进入 if(Ticket<=100) 这里的时候,开始睡觉,另一个线程也进入了,然而只有一张票可以卖了,这是里面又有两个线程,造成了卖了 101 张票,如何解决这个问题呢?
 
我们可以通过同步块或同步方法来解决这个问题
两个线程修改共享资源时会出现数据的不一致,为避免这种现象采用对访问的线程做限制的方法。利用每个对象都有一个 monitor( 锁标记 ) ,当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。
1 Synchronized 修饰代码块
  public void push(char c){
        synchronized(this){
        ...
        }
   }
对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块
2 Synchronized 修饰方法
   public synchronized void push(char c) {
      ...
      }
对当前对象的加锁,只有拿到锁标记的对象才能执行该方法
注:方法的 Synchronized 特性本身不会被继承,只能覆盖。
线程因为未拿到锁标记而发生阻塞进入锁池(lock pool)。每个对象都有自己的一个锁池的空间,用于放置等待运行的线程。由系统决定哪个线程拿到锁标记并运行。
 
上面的例子只有一个线程去卖票,不符合我们程序的要求,用以下方式解决
/**
 * 知识点:
 * 同步方法
 * 程序目标:
 * 两个线程共享 100 张票去销售这些票
 */
package MY.module09.tarena.synchronizdes;
 
public class TestSaleTickets3 {
 
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              SaleTickets st=new SaleTickets();
              Thread t1=new Thread(st);
              Thread t2=new Thread(st);
              t1.start();
              t2.start();
       }
 
}
class SaleTickets implements Runnable{
       private int Tickets=1;
       synchronized public void saleTicket() throws InterruptedException{
              String name=Thread.currentThread().getName();
              if(Tickets<=100){
                     Thread.sleep(1);
                     System.out.println(name+": 销售了第 "+Tickets+++" ");
              }
       }
       public void run() {
              // TODO Auto-generated method stub
              while(true){
                     try {
                            this.saleTicket();
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                     }
              }
       }
      
}
 
死锁的形成:
/**
 * 知识点:
 * 死锁
 * java 文件说明:
 * ResouceA.java
 * ResouceB.java
 * TestResouce.java
 * 程序目标:
 * 让线程类的对象调用 A 类的 f1 方法,并传 B 类的引用过去,在 f1 方法中
 * 会调用 B 类的方法,然而,当进入 f1 方法后,
 * 该线程将休眠一会,目的是让另一个线程进入 B 类的 f2 方法,这个 f2 方法中
 * 也会调用 A 类的一个方法,可是他们都会互相等待对方释放自己手中的
 * 锁,因为这些方法都是同步方法,从而造成死锁,谁也不会释放资源
 */
package MY.module09.tarena.synchronizdes.sisuo;
 
public class TestResource implements Runnable{
       private ResouceA a;
       private ResouceB b;
      
       public TestResource() {
              a=new ResouceA();
              b=new ResouceB();
              new Thread(this).start();
              a.f1(b);
       }
 
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              TestResource t=new TestResource();
       }
 
       public void run() {
              // TODO Auto-generated method stub
              b.f2(a);
       }
 
}
package MY.module09.tarena.synchronizdes.sisuo;
 
public class ResouceA {
       synchronized public void f1(ResouceB b){
              String name=Thread.currentThread().getName();
              System.out.println(name+" f1()");
              try {
                     Thread.sleep(1);
              } catch (InterruptedException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              }
              b.rb();
       }
      
       synchronized public void ra(){
              System.out.println(" 先把货给我,我再给你钱 ");
       }
}
package MY.module09.tarena.synchronizdes.sisuo;
 
public class ResouceB {
       synchronized public void f2(ResouceA a){
              String name=Thread.currentThread().getName();
              System.out.println(name+" f2()");
              try {
                     Thread.sleep(1);
              } catch (InterruptedException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              }
              a.ra();
       }
      
       synchronized public void rb(){
              System.out.println(" 先把钱给我,我再给你货 ");
       }
}
 
 
生产者 消费者模式:
/**
 * 知识点:
 * 生产-消费者模式 实现线程通讯
 * Java 文件说明:
 * Car.java :封装了生产和销售汽车的方法
 * Consumer.java :消费汽车
 * Producer.java :生产汽车
 * TestCommuniction.java :主程序
 * 程序目标:
 * 当库存没有货了,开始生产汽车,如果有货,就不能生产了
 * 只能销售,当没有库存了,便不能销售,通知开始生产
 */
package MY.module09.tarena.Communications;
 
public class TestCommunication {
       public void test(){
              Car car=new Car();
              Consumer cs=new Consumer(car);
              Producer pd=new Producer(car);
              Thread tr=new Thread(cs);
              pd.start();
              tr.start();
       }
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              TestCommunication t=new TestCommunication();
              t.test();
       }
 
}
package MY.module09.tarena.Communications;
 
public class Car {
       private String name;
       private double price;
       private boolean isFull = false;
       public Car() {
 
       }
       public Car(String name, double price) {
              this.name = name;
              this.price = price;
       }
synchronized public void put(String name,double price){
              if(isFull==true){// 如果库房已经满了
                     try {
                            wait();
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                     }
              }
              this.name=name;
              this.price=price;
              System.out.println(" 生产出一量汽车 ");
              isFull=true;
              notify();
       }
synchronized public void get(){
              if(!isFull){
                     try {
                            wait();
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                     }
              }
              System.out.println(name+":"+price);
              System.out.println(" 卖出一量车 ");
              isFull=false;
              notify();
       }
       public String getName() {
              return name;
       }
       public void setName(String name) {
              this.name = name;
       }
       public double getPrice() {
              return price;
       }
       public void setPrice(double price) {
              this.price = price;
       }
      
}
package MY.module09.tarena.Communications;
 
public class Consumer implements Runnable{
       private Car car;
 
       public Consumer(Car car) {
              super();
              // TODO Auto-generated constructor stub
              this.car = car;
       }
 
       public void run() {
              // TODO Auto-generated method stub
              while(true){
                     car.get();
              }
       }
      
}
package MY.module09.tarena.Communications;
 
public class Producer extends Thread{
       private Car car;
 
       public Producer(Car car) {
              super();
              // TODO Auto-generated constructor stub
              this.car = car;
       }
       public void run(){
              while(true){
                     car.put("Benz",10000);
              }
       }
}
线程的生命周期:
1. new 线程类   :创建了一个线程
2. 线程对象 .start() :线程准备就绪状态
3.  线程的调度器(类)会给准备就绪的线程分配资源(时间片,变量,内存等),分配后线程就可以运行了: Running 状态也就是运行状态,也可以拿走这些资源,拿走后回到了准备就绪状态
4. 死亡状态:死后不能复活,当线程执行完毕后,死亡
5. 阻塞状态:
锁池,当有一个线程拿到锁了,另一个线程也想要得到,那他先在锁池中等待,等待另一个线程释放锁后,和其他线程共同抢这个锁,释放锁后回到可运行状态,等待调度器分配资源,可以再次抢这个锁
等待池,当线程遇到 wait 方法后,进入等待池,这个时候,需要其他线程使用 notify 或者 notifyall 方法唤醒,唤醒后回到锁池中,等待其他线程释放锁,当其他线程释放锁的时候,共同争夺对象锁,等待调度器分配资源,可以再次抢这个锁
notify: 随机挑选一个线程去唤醒(只能唤醒一个)
notifyall: 每个线程都叫一遍,最终只有一个被唤醒
休眠: sleep 方法,进入休眠状态,睡醒后回到可运行状态,等待调度器分配资源,使用 interrupt() 方法可以将正在休眠的线程打醒
join() 阻塞状态:加入到一个线程
 
线程的停止:
Thread.stop();
/**
 * 程序目标:
 * 让线程停止
 * 有个 Stop 方法,但是不推荐使用
 * 我们可以用一个标记 boolean 类型的来控制程序的结束
 */
package MY.module09.tarena.ThreadStop;
 
public class ThreadStop extends Thread{
       private boolean stop=true;
       public void SStop(){
              stop=false;
       }
       /**
        * @param args
        */
       @SuppressWarnings("deprecation")
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              ThreadStop t=new ThreadStop();
              t.start();// 有可能 t0 先执行,也有可能 man 执行
              try {
                     t.sleep(500);// t0 线程去睡觉
              } catch (InterruptedException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
              }
              System.out.println("man running");
              t.SStop();
 
       }
       public void run(){
              String name=Thread.currentThread().getName();
              while(stop){
                     System.out.println(name+":runing......");
              }
       }
}
线程的优先级:
依赖于平台的,每个平台的优先级都不同
线程对象 .setName 给线程起名字
线程对象 .setPriority(Thread.Max);
/**
 * 程序目标:
 * 测试线程设置姓名和优先级
 */
package MY.module09.tarena.priority;
 
public class TestPriority implements Runnable{
       private int i=1;
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              TestPriority tp1=new TestPriority();
              Thread t1=new Thread(tp1);
              Thread t2=new Thread(tp1);
              t1.setName("t1");
              t2.setName("t2");
              t1.setPriority(Thread.MAX_PRIORITY);
              t2.setPriority(Thread.MIN_PRIORITY);
              t1.start();
              t2.start();
       }
       public void run(){
              String name=Thread.currentThread().getName();
              while(i<=500){
                     System.out.println(name+":"+i);
                     i++;
              }
       }
}
join() 方法:
也叫等死,等插入的线程运行完死掉,再接着执行
/**
 * 知识点:
 * join()
 * 程序目标:
 * 两个线程,一个循环 2000 次,一个循环 200 次,当循环 2000 次的那个
 * 线程,循环到 100 次的时候,让另一个线程插入,等他循环完,死掉
 * 后,继续循环,直到死掉
 */
package MY.module09.tarena.join;
 
public class TestJoin implements Runnable{
 
       /**
        * @param args
        */
       public static void main(String[] args) {
              // TODO Auto-generated method stub
              TestJoin tj=new TestJoin();
              Thread t1=new Thread(tj);
              t1.start();
              int i=0;
              String name=Thread.currentThread().getName();
              while(i<200){
                     if(i==100){
                            try {
                                   t1.join();
                            } catch (InterruptedException e) {
                                   // TODO Auto-generated catch block
                                   e.printStackTrace();
                            }
                     }      
                     System.out.println(name+":"+i++);
              }
       }
 
       public void run() {
              // TODO Auto-generated method stub
              int j=0;
              String name=Thread.currentThread().getName();
              while(j<2000){
                     System.out.println(name+":"+j++);
              }
       }
 
}
 

interrupt():打醒正在休眠的线程

/**

 * 知识点:

 * Interrupt():打醒正在休眠的线程

 * currentTimeMillis():System中的方法,返回当前时间,以豪秒为单位

 * 程序目标:

 * 让一个线程睡20000豪秒,man线程睡2000豪秒,当man醒来,就打醒

 * 另一个线程

 */

package MY.module09.tarena.interrupt;

 

public class TestInterrupt extends Thread{

      

       /**

        * @param args

        */

       public static void main(String[] args) {

              // TODO Auto-generated method stub

              TestInterrupt tl=new TestInterrupt();

              tl.start();

              try {

                     Thread.sleep(2000);

              } catch (InterruptedException e) {

                     // TODO Auto-generated catch block

                     e.printStackTrace();

              }

              System.out.println("我醒了,你也别想睡");

              tl.interrupt();

       }

       public void run(){

              long startTime=System.currentTimeMillis();

              System.out.println("我要睡觉了");

              try {

                     Thread.sleep(20000);

              } catch (InterruptedException e) {

                     // TODO Auto-generated catch block

                     long endTime=System.currentTimeMillis()-startTime;

                     System.out.print("刚睡一会就被吵醒了"+endTime);

              }

       }

}

 

类锁:

什么是类锁?有什么用?什么情况下需要用类锁?

如果我们要访问静态的变量,是需要通过一个静态方法的,那么给静态方法加上synchronized,做数据并发处理,这个时候,线程得到的锁不是对象的锁,而是这个类的锁,因为这是synchronized修饰的是静态方法,静态方法属于类,不属于某一对象

/**

 * 知识点:

 * 类锁

 * 程序目标:

 * 两个线程访问另一个类的静态变量,当方法另一个类的静态方法的时候

 * 得到静态变量的值,方法中的第一句是让静态变量加1,如果不使用同步

 * 方法的话,因为休眠的缘故,会出现线程不安全的情况,所以必须在静态

 * 方法上得加上琐,这个锁是类锁

 */

package MY.module09.tarena.classlock;

 

public class TestClassLock {

       private static int i=1000;

       public synchronized static int get(){

              i++;

              try {

                     Thread.sleep(1);

              } catch (InterruptedException e) {

                     // TODO Auto-generated catch block

                     e.printStackTrace();

              }

              return i;

       }

       /**

        * @param args

        */

       public static void main(String[] args) {

              // TODO Auto-generated method stub

              B t1=new B();

              B t2=new B();

              t1.start();

              t2.start();

       }

 

}

class B extends Thread{

       TestClassLock tcl=new TestClassLock();

       public void run(){

              System.out.println(tcl.get());

       }

}

懒汉单例模式:

为什么叫懒汉单例模式呢?

因为在属性中先不new对象,得别人需要的时候,在new对象

通过类锁可以实现懒汉单例模式

/**

 * 知识点:

 * 类锁,懒汉单例模式

 */

package MY.module09.tarena.danli2;

 

public class TestDanli {

       private static TestDanli td=null;

      

       private TestDanli() {

              // TODO Auto-generated constructor stub

              System.out.println("结婚了");

       }

      

       public synchronized static TestDanli getDanli(){

              if(td==null){

                     try {

                            Thread.sleep(1);

                     } catch (InterruptedException e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                     }

                     td=new TestDanli();

              }

              return td;

       }

 

       /**

        * @param args

        */

       public static void main(String[] args) {

              // TODO Auto-generated method stub

              B t1=new B();

              B t2=new B();

              t1.start();

              t2.start();

       }

 

}

class B extends Thread{

       public void run(){

              TestDanli.getDanli();

       }

}

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值