java-多线程

attetion:观看黑马视频笔记笔记总结

快捷键集合:

1.MyThread继承Thread,重写run方法 选中Mythread,ctrl+1

2.选中某个要输出的东西,然后alt+/就可以输出了

3.出现异常
eg:
Thread.Sleep(1000)
选中它,ctrl+1->add throws declaration

4.Ctrl+shift+t看源码

eg:看Thread的源码
搜索Thread就可以了,选中Thread ctrl+O

Sleep是一个静态方法,所以类名.Sleep()就可以运行了???

5.zijiemaduixiang.

 

 

 

多线程
* 01.什么是线程
         * 线程是程序执行的一条路径, 一个进程中可以包含多条线程
         * 多线程并发执行可以提高程序的效率, 可以同时完成多项工作
* 02.多线程的应用场景
        * 红蜘蛛同时共享屏幕给多个电脑
        * 迅雷开启多条线程一起下载
        * QQ同时和多个人一起视频

 02.2多线程并行和并发的区别
        * 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
        * 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,                                由于时间间隔较短,使人感觉两个任务都在运行
 

 

03_多线程(Java程序运行原理和JVM的启动是多线程的吗)
* A:Java程序运行原理

for(int i=0;i<1000000;i++)
  {
     new Demo();
  }
  for(int i=0;i<100000;i++)
  {
  system.printIn("我是多线程“);
  }
  class Demo{
  public void finalize()
  {
  system.printIn(垃圾被清扫);
 
  }//两者不是先执行一个完后,在执行另外一个,而是相互间隔执行的。

* B:JVM的启动是多线程的吗
 * JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

 

04_多线程程序实现的方式1
* 1.继承Thread
 * 定义类继承Thread
 * 重写run方法
 * 把新线程要做的事写在run方法中
 * 创建线程对象
 * 开启新线程,(
就是调用start()方法,相当于一个发令枪) 内部会自动执行run方法

public class Demo_2Thread {

	public static void main(String[] args) {
	MyThread mt = new MyThread();		//4,创建Thread类的子类对象
	mt.start();			        //5,开启线程
		
	for(int i = 0; i < 1000; i++) {
	System.out.println("bb");
	}
	}

}

class MyThread extends Thread {		//1,继承Thread
	public void run() {		//2,重写run方法
	for(int i = 0; i < 1000; i++) {	//3,将要执行的代码写在run方法中
	System.out.println("aaaaaaaaaaaa");
	}
 }
}

 

 

05_多线程(多线程程序实现的方式2)

官话:

创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。
 2.实现Runnable
 * 定义类实现Runnable接口
 * 实现run方法
 * 把新线程要做的事写在run方法中
 * 创建自定义的Runnable的子类对象
 * 创建Thread对象, 传入Runnable
 * 调用start()开启新线程, 内部会自动调用Runnable的run()方法



public class Demo3_Thread {

	public static void main(String[] args) {
		MyRunnable mr = new MyRunnable();//4,创建Runnable的子类对象
		//Runnable target = mr;	
		Thread t = new Thread(mr);   //5,将其当作参数传递给Thread的构造函数
		t.start();		   //6,开启线程
		
		for(int i = 0; i < 1000; i++) {
			System.out.println("bb");
		}
	}

}

class MyRunnable implements Runnable {		//1,定义一个类实现Runnable

	@Override
	public void run() {			//2,重写run方法
		for(int i = 0; i < 1000; i++) {		//3,将要执行的代码写在run方法中
			System.out.println("aaaaaaaaaaaa");
		}
	}
	
}

 

补充:

start是开启线程的,开启后,会自动调用里面的run方法(里面是执行的代码)
start相当于发令枪,run方法是运动员的各种表现(
是你还是要写run方法的)
不管怎么样的目的都是达到看到运动员的表演,不过start是他们的开关

 

 

06_多线程(实现Runnable的原理)
* 查看源码
 * 1,看Thread类的构造函数,传递了Runnable接口的引用

按住Thread,Ctrl:


 * 2,通过init()方法找到传递的target给成员变量的target赋值

This.target=Target也就是说局部变量赋值给了成员变量

点击target

可以看出这个成员变量使用Runnable声明的

 

 

结论:mr这个对象的值一层的一层的传递到了Runable target这个值
但这个值有什么作用

按住Target  ctrl+o搜索run方法


 * 3,查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法

 

target.run就是mr.run(),

target.run相当于 Runnable target=mr(父类的引用指向子类对象)

target.run编译时父类,运行是子类************************************

 

 


07_多线程(两种方式的区别)
* 查看源码的区别:
 * a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
 * b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法

* 继承Thread
 * 好处是:可以直接使用Thread类中的方法,代码简单
 * 弊端是:如果已经有了父类,就不能用这种方法?????
* 实现Runnable接口

实现线程呢,第二个方式由于没有继承thread,要如何实现呢
1.把这个Runnable的子类对象传到Thread的构造方法中,
传递到里面的话,因为这个子类对象中会包含run方法,
有一个判断,如果子类对象不是空的话,JVM自动会调用run方法 

 * 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的(understand)
 * 弊端是:不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到Thread的方法,代码复杂
 



08_多线程(匿名内部类实现线程的两种方式)
为什么用匿名内部类,不用找一个类去继承thread类了,值接new  Thread 或者new Runnable接口就可以了,
然后只需要重写里的一个方法就可以了,简便????
* 继承Thread类
   
  new Thread() {             //1,new 类(){}继承这个类,这就是继承Thread这个类
   public void run() {           //2,重写run方法
    for(int i = 0; i < 3000; i++) {       //3,将要执行的代码,写在run方法中
     System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
    }
   }
  }.start();                                                          //开启线程

****上面的整个就代表thread的子类对象
  
  
* 实现Runnable接口
   
  new Thread(

  new Runnable()   {          //1,将Runnable的子类对象传递给Threadde 构造方法
   public void run() {           //2,重写run方法
    for(int i = 0; i < 3000; i++) {       //3,将要执行的代码,写在run方法中
     System.out.println("bb");
    }
   }
  }

  ).start();                                                  
  

 

09_多线程(获取名字和设置名字)
* 1.获取名字
 * 通过getName()方法获取线程对象的名字
* 2.设置名字
 * 通过构造函数可以传入String类型的名字
 *
   new Thread("xxx") {
    public void run() {
     for(int i = 0; i < 1000; i++) {
      System.out.println(this.getName() + "....aaaaaaaaaaaaaaaaaaaaaaa");
     }
    }
   }.start();
   ///******这的This就相当于匿名内部类的对象谁来调用我我就代表谁


   new Thread("name") {
    public void run() {
     for(int i = 0; i < 1000; i++) {
      System.out.println(this.getName() + "....bb");
     }
    }
   }.start();
   
   
 * 通过setName(String)方法可以设置线程对象的名字
 *下面的方式是父类的引用指向子类对象:
   Thread t1 = new Thread() {
    public void run() {
     for(int i = 0; i < 1000; i++) {
      System.out.println(this.getName() + "....aaaaaaaaaaaaaaaaaaaaaaa");
     }
    }
   };
   
   Thread t2 = new Thread() {
    public void run() {
     for(int i = 0; i < 1000; i++) {
      System.out.println(this.getName() + "....bb");
     }
    }
   };
   t1.setName("芙蓉姐姐");
   t2.setName("凤姐");
   
   t1.start();
   t2.start();

 

10_多线程(获取当前线程的对象)

用Getname的话,因为是thread的方法,runnable用不了,上面的08有例子 想办法获得当前正在执行的线程

//与runnable接口有关系
* Thread.currentThread(), 利用它主线程也可以获取
 *
   new Thread(

     new Runnable() {
    public void run() {
     for(int i = 0; i < 1000; i++) {
      System.out.println(Thread.currentThread().getName() + "aaaaaaa");
     }
    }
   }

).start();
   Thread.currentThread().setName("我是主线程");     //获取主函数线程的引用,并改名字
   System.out.println(Thread.currentThread().getName());  //获取主函数线程的引用,并获取名字

11_多线程(休眠线程)
* Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000纳秒 1000000000

Sleep是一个静态方法,所以类名.Sleep()就可以运行.

new Thread() {
    public void run() {
     for(int i = 0; i < 10; i++) {
      System.out.println(getName() + "...aaaaaaaaaaaaaa");
      try {
       Thread.sleep(10);
        //这个时候打印的话是10个a,10个b,但如何体现多线程呢,CPU执行的太快了
        //想要看出切换的过程,让cpu到执行我这个程序时候睡一会,
        //但是这里会有异常,因为正走着走着,睡着了,属于中断异常
        
        //这里异常是抛还是抓呢,Thread的run方法是没有抛异常的,
        //new Thread这里面的东西是Thread的子类对象,属于而儿子级别      
       //所以,父亲坏了,儿子不能比父亲更坏的例子
        //儿子必须自己处理,用try  catch
        //当cpu执行到这的时候,发现他睡了,就把  执行权切换给第二个,第二个也是睡的,有切换回去,
        //如果发现第一个醒了,就把执行权利给第一个
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
     }
    }
   }.start();
   
   new Thread() {
    public void run() {
     for(int i = 0; i < 10; i++) {
      System.out.println(getName() + "...bb");
      try {
       Thread.sleep(10);
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
     }
    }
   }.start();


   
   
12_多线程(守护线程)
* setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
//非守护线程;帅
//守护线程:兵,象,马
 *

 Thread t1 = new Thread() {
    public void run() {
     for(int i = 0; i < 50; i++) {
      System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
      try {
       Thread.sleep(10);
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
     }
    }
   };
   
   Thread t2 = new Thread() {
    public void run() {
     for(int i = 0; i < 5; i++) {
      System.out.println(getName() + "...bb");
      try {
       Thread.sleep(10);
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
     }
    }
   };
   
   t2.setDaemon(true);      //将t1设置为守护线程
   
   t1.start();
   t2.start();


    //t2.setDaemon(true);      //参数传递为true将t2设置为守护线程
   //如果非守护线程挂掉了。那么守护线程也会有一段时间缓冲区自杀的过程消耗时间,会有个时间缓冲,也会随之死亡。
   //所以像上面的例子中,aaa的次数执行完成后,bb会过上一小会才会挂掉.

 


   
13_多线程(加入线程)
* join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续(插队)
* join(int), 可以等待指定的毫秒之后继续

//匿名内部类在使用它的局部变量的时候必须用final修饰,因为局部变量的生命周期跟不上,

 

final Thread t1 = new Thread() {
    public void run() {
     for(int i = 0; i < 50; i++) {
      System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
      try {
       Thread.sleep(10);
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
     }
    }
   };
   
   Thread t2 = new Thread() {
    public void run() {
     for(int i = 0; i < 50; i++) {
      if(i == 2) {
       try {
        //t1.join();      //插队,加入,插队过来是一个异常
        t1.join(30);      //加入,有固定的时间,过了固定时间,继续交替执行
        //这个是join的重载方法,里面 传递的是毫秒值
        Thread.sleep(10);
       } catch (InterruptedException e) { 
        
        e.printStackTrace();
       }
      }
      System.out.println(getName() + "...bb");
     
     }
    }
   };//这个是属于一个匿名内部类,t1属于一个局部变量,匿名内部类在使用一个它所在方法中的局部变量的 时候必须用final修饰
    ///匿名内部类就是我们的局部内部类
   t1.start();
   t2.start();


   

 

14_多线程(礼让线程)


* yield让出cpu

Thread.yiead();

 

15_多线程(设置线程的优先级)
* setPriority()设置线程的优先级
看源码:ctrl+shift_+t
下来ctrl+o

t1.setPriority(Thread.MIN_PRIOITY)

//将t1这条线程设置为最小优先级

video补充笔记

 

16_多线程(同步代码块)
* 1.什么情况下需要同步
 * 当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
 * 如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
* 2.同步代码块
 * 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
 * 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的

 

注意:

final  Printer pri=new Printer();//final 不要忘记了匿名内部类用方法的局部变量
Demo d = new Demo();

锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象

就是在()直接写一个new Demo()。

attetion:location of Demo.

It shoud be after Class.
          

public static vodi main(String args[])
    {
       final  Printer pri=new Printer();//final 不要忘记了,匿名内部类用方法的局部变量
      
       new Thread()//创建一个线程
       {
        public vodi run()
     {
      while(true)
      {
      pri.print1();
     
     
      }
     }
       }.start();
      
       new Thread()//创建另外一个线程
       {
        public vodi run()
     {
      while(true)
      {
      pri.print2();
     
     
      }
     }
       }.start();
      
         
    }
           
   class Printer {
    Demo d = new Demo();
    public static void print1() {
     synchronized(d){    //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
      System.out.print("西");        //锁对象不能是匿名对像,已经为匿名对象不是同一对像,类似于直接在()中直接传递new Demo()这种,是不可以的。
      System.out.print("安");
      System.out.print("邮");
      System.out.print("电");
      System.out.print("\r\n");
     }
    }
 
    public static void print2() { 
     synchronized(d){ 
      System.out.print("西");
      System.out.print("安");
      System.out.print("财");
      System.out.print("经");
      System.out.print("\r\n");
     }
    }
   }


   

 

 

24.17_多线程(同步方法)
//public synchroinzed void print1()//同步方法只需要在方法上加上synchroinzed;
* 使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
//非静态的同步方法的 锁的对像是神马?

//答案:锁对象是this

 

静态方法中的锁对象能不能是this呢,
不能,因为静态是优先于对象存在的,静态方法中
是不可以定义this和super的

 

this是当你去创建对象的时候才有值的

 静态方法是随着类的加载而加载的,类加载的时候有对象,

对象就是那个字节码对象,就是class 后面的东西.class,就是类名.class

//西安邮电中有一个锁对象,第二个财经可以在class后定义一个静态的锁对象,然后传递进来,也可以用字节码对象,为了一致选择第一个
 

class Printer2
{
Demo d=new Demo();
//非静态的同步方法的 锁的对像是神马,如果程序运行时结果不穿插,说明则是Object对象,任意的,如d;但是结果是乱序的
//答案:
public synchroinzed void print1()         //该方法中隐含了一个指针,this,谁调用我我就指向谁
{
     System.out.print("西");
     System.out.print("安");
     System.out.print("邮");
     System.out.print("电");
     System.out.print("\r\n");

}
public void print2()
{
synchroinzed(this)
{
    System.out.print("西");
    System.out.print("安");
    System.out.print("财");
    System.out.print("经");
    System.out.print("\r\n");

}
}


//第二个加上了this结果就正确了

  class Printer {
   public static void print1() {
    synchronized(Printer.class){    //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
     System.out.print("西");
     System.out.print("安");
     System.out.print("邮");
     System.out.print("电");
     System.out.print("\r\n");

    }
   }
   /*
    * 非静态同步函数的锁是:this
    * 静态的同步函数的锁是:字节码对象
    */
   public static synchronized void print2(Printer.class) { 
    System.out.print("西");
    System.out.print("安");
    System.out.print("财");
    System.out.print("经");
    System.out.print("\r\n");
   }
  }







//静态的:
class Printer2
{
public static synchroinzed void print1()         {
     System.out.print("西");
     System.out.print("安");
     System.out.print("邮");
     System.out.print("电");
     System.out.print("\r\n");

}
public void print2()
{
synchroinzed(Printer.class)
{
    System.out.print("西");
    System.out.print("安");
    System.out.print("财");
    System.out.print("经");
    System.out.print("\r\n");

}
}
//西安邮电中有一个锁对象,第二个财经可以在class后定义一个静态的锁对象,然后传递进来,也可以用字节码对象,为了一致选择第一个


 

 

 

18_多线程(线程安全问题)

* ******多线程并发操作同一数据时, 就有可能出现线程安全问题
* 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作

main()
{
  new Ticket.start();
  new Ticket.start();
  new Ticket.start();
  new Ticket.start();
 
 }


1.上面的问题会导致每个卖400张,4个对象,每个人100张,所以加上static使之成为共享的
private  static int ticket=100;
2.但是问题不明显,加上Sleep后来看一下,同时加上try catch (选中sleep选择第一个)但会导致卖到-1000多张票,


解决办法:
加上睡眠的方法是模拟那些行执行n多行程序的情况
出现问题的原因:

运行到ticket=1,跳转到try的Sleep中

遇到Sleep,线程一,同时,其他三个线程一样都睡觉
此时线程一的时候睡醒了,ticket=0,其他三个过来就会从0开始不断地往下-,

他就会执行输出语句ticket--,其他三个一样,跳过ticket==0这一条件

3.加上<=0也会有问题
跟上面情况一样,可能线程3睡醒了,但是他还是会执行ticket--的操作也会有几个负数,然后再去判断if的条件,还是会有几个负数
4.此时涉及到同步的问题:

       if(tickets <= 0)
        break;
       try {
        Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
       } catch (InterruptedException e) {
        
        e.printStackTrace();
       }
       System.out.println(getName() + "...这是第" + tickets-- + "号票");
       


       给这段加上同步
       令if的ticket的和输出的ticket加上同步代码块
       synchronized(this),加到if的前面
       但是问题依旧
       因为this代表new ticket,但是他有四个,每个线程都会有自己的对象,每次new一次都会是不同的对象
       加上Ticket.class就可以了给同步代码快的括号中
        加上static  Object obj也是可以的额,但是第二个最好。

 

 


class Ticket extends Thread{
       
  private int ticket=100;
  public void run()
  {
  
  while(true)
  
  {
   if(ticket==0)
   {
   break;
   }
   system.out printIn(getname()+"This is the "+  ticket--"tickets");
  }
  }

}
   
   public class Demo2_Synchronized {

    /**
     * @param args
     * 需求:铁路售票,一共100张,通过四个窗口卖完.
     */
    public static void main(String[] args) {
     TicketsSeller t1 = new TicketsSeller();
     TicketsSeller t2 = new TicketsSeller();
     TicketsSeller t3 = new TicketsSeller();
     TicketsSeller t4 = new TicketsSeller();
     
     t1.setName("窗口1");
     t2.setName("窗口2");
     t3.setName("窗口3");
     t4.setName("窗口4");
     t1.start();
     t2.start();
     t3.start();
     t4.start();
    }
   
   }
  //加static让四个线程共享的变量 
   class TicketsSeller extends Thread {
    private static int tickets = 100;
    static Object obj = new Object();
    public TicketsSeller() {
     super();
     
    }
    public TicketsSeller(String name) {
     super(name);
    }
    public void run() {
     while(true) {
      synchronized(obj) {
       if(tickets <= 0)
        break;
       try {
        Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
       } catch (InterruptedException e) {
        
        e.printStackTrace();
       }
       System.out.println(getName() + "...这是第" + tickets-- + "号票");
      }
     }
    }
   }

 

19_多线程(火车站卖票的例子用实现Runnable接口)

//tickets不用加static,因为对象只创建了一次,一个mt

class MyTicket implements Runnable{
private int tickets=100;//这里不用定义为static , 因为对象只创建了一次
public void run() {
     while(true) {
      synchronized(Ticket.class) {//或者this都是可以的
       if(tickets <= 0)
        break;
       try {
        Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
       } catch (InterruptedException e) {
        
        e.printStackTrace();
       }
       System.out.println(getName() + "...这是第" + tickets-- + "号票");
      }//上面的哪一行加上Thread.currentThread.getName(),因为不是Thread的子类
     }
    }}}

main()
  {
       MyTicket mt=new MyTicket();
       new thread(mt).start();
       new thread(mt).start(); 
       new thread(mt).start(); 
       new thread(mt).start();    
  }


  但是另外一种情况
  Thread t1=new Thread(mt);
  t1.start();
  t1.start();
  t1.start();
  t1.start();
  这样把mt传进来,这样会有异常,但是是非法的,一个线程多次启用是违法的,就像是刘翔跑完100,在重新跑100的问题。
 

 

 
 
.20_多线程(死锁)
* 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
 * 尽量不要嵌套使用
  
   

private static String s1 = "筷子左";
   private static String s2 = "筷子右";
   public static void main(String[] args) {
    new Thread() {
     public void run() {
      while(true) {
       synchronized(s1) {
        System.out.println(getName() + "...拿到" + s1 + "等待" + s2);
        synchronized(s2) {
         System.out.println(getName() + "...拿到" + s2 + "开吃");
        }
       }
      }
     }
    }.start();
    
    new Thread() {
     public void run() {
      while(true) {
       synchronized(s2) {
        System.out.println(getName() + "...拿到" + s2 + "等待" + s1);
        synchronized(s1) {
         System.out.println(getName() + "...拿到" + s1 + "开吃");
        }
       }
      }
     }
    }.start();
   }

21_多线程(以前的线程安全的类回顾)
* A:回顾以前说过的线程安全问题
    ctrl+shift+t-->ctrl+o->看每一个方法前面加没加synchronized
 * 看源码:Vector,StringBuffer,Hashtable,Collections.synchroinzed(xxx)
 * Vector是线程安全的,ArrayList是线程不安全的
 * StringBuffer是线程安全的,

  *StringBuilder是线程不安全的
 * Hashtable是线程安全的,

  *HashMap是线程不安全的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值