单线程与多线程

单线程:按照程序的代码顺序依次执行  
多线程:每次运行结果都不一样
    1.程序:计算机语言的指令集合
    2.进程:正在执行的程序
    3.线程:进程中某个被执行的片段,cup会在不同的线程之间切换,线程是程序执行的最小单位(程序运行要内存,把程序加载到内存,还要cpu,cpu要进行计算)

第一(面试):实现多线程:构造函数默认调用了父类无参的构造
    1.继承Thread(线程),Thread封装的是程序中的某个片段,因此别的类继承他以后,也能达到这个目的(多线程的目的)
    2.重写run方法,在run方法中写的是多个线程都具备的功能(想要达到多线程效果的方法写入run方法中)run方法的功能是实现多线程
    3.start(启动线程)方法,用这个方法以后,jvm会自动调用run方法
第二(面试):第二种实现方式:调用了带一个或者带两个的参数的构造函数
   由于Thread是runnable接口的实现类,因此我的类实现runnable接口也能实现多线程(该接口中只有一个方法run,因此用接口的时候一般需要在main方法中引入Thread对象)
     1.实现runnable接口
     2.重写run方法
     3.创建Thread对象(查线程池)
     3.调用Thread带参构造。用我的类的对象创建线程,线程启动的时候自动调用run方法(thread t1=new thread(u1))

第三(面试):线程安全(线程同步):线程切换具有随机性,有时候线程正在执行一半就切换了,这样就不同步了。
      临界区资源:多个线程共享的资源(一段代码,数据),线程同步就是对临界区资源进行保护。
              对于临界区资源必须保证多线程对临界区资源的执行结果和单线程是一直的(线程同步)
    线程同步的原理:给临界区资源加锁,利用的是java中(引用类型)对象的锁
    1.实现线程同步:使用同步代码块 :synchronized:同步(加锁),退出大括号以后锁就打开了。同步以后并发性降低了。
       synchronized(“hello”),其中hello就是对象(字符串就是引用类型的对象),而且就有这一个对象(只有他有钥匙,能开锁)
    2.实现线程同步:使用同步方法 :把我自己的方法用synchronized修饰,相当于给整个方法加锁。然后将这个方法写入run方法中去执行线程。
        方法中的synchronized调用的相当于是this,this表示每一个对象,我new过几次现在就有几个对象(有多个钥匙了),因为不能实现同步。
        一个实现类的对象封装多个线程。
3.Thread.currentThread()是返回当前线程对象,这是个静态方法。。Thread.currentThread().getName()是得到当前线程的名字


注释:同步代码块能够保证本次循环的线程安全
      同步方法能够保证整个循环的线程安全
run方法实现多线程的时候不管是否同步,如果想同步,就要程序员使用同步代码块或者同步方法


第四:例题:四个售票窗口(可以同时卖票,因此是多线程),一个票池(共享的,临界区资源)
对于多线程,是用继承父类还是用实现接口?
答:由于继承只能是单继承(继承资源是有限的),所以一般用接口实现
   如果需要多个线程共享某个资源,就用实现接口的方法实现多线程,因为可以使用同步方法进行同步。(如果用继承的话,就不能y用同步方法了,因为继承会产生多个对象,同步方法需要this)
出现的个数比想象的多,是因为有的线程判断成功了,只是没执行,下次在执行的时候就不会判断了(因为判断过了)


第五:线程的优先级:得到线程优先级getPriority,返回值是个整数(常量),最小优先级是1,最大是10,默认优先级是5。数字越大优先级又高,高优先级获得的线程运行的几率,不是绝对的
 设置(更改)线程优先级:setPriority(Thread.MAx...)
第六:线程的状态(面试会考):
声明周期:
被创键,start,运行态,wait,等待态,notify,唤醒态
                       ,阻塞态(死锁),阻塞态的:例如TCP协议中的accecp方法,一个监听器,开启之后就是阻塞状态,当客户端有请求来的时候变成运行态
                         消亡


两个人针对一个账号,因为属性需要共享,因此用接口,(因为用接口,在创建对象的时候只需创建一个)

 第二:代码实现

1.调用带一个参数的构造函数

package lianxi.Runnable;
/**
 *以实现Runnable接口的方式实现线程
 */
public class runnableTest1 implements Runnable{
 private String name;
 private String role;
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getRole() {
  return role;
 }
 public void setRole(String role) {
  this.role = role;
 }
 public runnableTest1() {
  super();
 }
 public runnableTest1(String name, String role) {
  super();
  this.name = name;
  this.role = role;
 }
  public void move(){
     for(int i=0;i<10;i++){
      System.out.println(this.name+"移动了"+i+"米");
     }
    }
 @Override
 public void run() {//此时相当于三个线程共同用这个run方法
  move();
  
 }
 
 public static void main(String[] args) {
  runnableTest1 r1=new runnableTest1("张三","警察");
  runnableTest1 r2=new runnableTest1("lisa","警察");
  runnableTest1 r3=new runnableTest1("拉搜救","土匪");
  Thread th1=new Thread(r1);//创建线程,这是调用线程带一个参数的构造函数
  Thread th2=new Thread(r2);
  Thread th3=new Thread(r3);
  th1.start();
  th2.start();
  th3.start();
 }
}

2.调用带两个参数的构造函数

package lianxi.Runnable;

public class runnableTest2 implements Runnable{
 private String name;
 private String role;
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getRole() {
  return role;
 }
 public void setRole(String role) {
  this.role = role;
 }
 public runnableTest2() {
  super();
 }
 public runnableTest2(String name, String role) {
  super();
  this.name = name;
  this.role = role;
 }
 /**  当for循环中的内容不止一句的时候,由于线程的时间片轮转机制,经常会遇到当前线程还没有完成一个完整的for循环,
  *   就转换成其他的线程开始执行这个方法了。也就是说我的这个线程刚执行一半,就轮到其他线程了,为了解决这个问题,就
  *   出现了线程同步(安全)的概念,线程同步分成两个内容,一是方法同步,一个是代码块同步。提高了线程安全,缺降低了线程的并发性
  *   不管是代码块同步还是方法同步,synchronized加锁(同步)的时候都需要一个对象。
  *  
  *  @方法的作用:线程中的同步方法。这里是把方法同步(加锁)了,因此只有这个线程执行完这个方法(完成整个for循环)才会轮到下一个线程。
  *          synchronized在方法同步中需要一个对象,这个对象默认就是this,因此要保证main方法中只创建一次对象,
  *           一个对象创建多个线程,就要用到线程构造方法中的两个参数的构造方法
  *  @作者:褚永也
  *  @日期:2014-7-17 下午2:56:35
  *  @返回值类型:void
  */
  public synchronized void move(){//同步(加锁)方法,这里默认调用的是this对象
     for(int i=0;i<10;i++){
      System.out.println(Thread.currentThread().getName()+" 射击第"+i+"发子弹.....");//得到当前线程的名字
      System.out.println(Thread.currentThread().getName()+" 第"+i+"发子弹,击中目标.");
     }
    }
 @Override
 public void run() {//此时相当于三个线程共同用这个run方法
  move();
  
 }
 public static void main(String[] args) {
  runnableTest2 r = new runnableTest2("张三","警察");//保证了只有一个对象
  Thread t1 = new Thread(r,"线程1");//调用线程的两个参数的构造方法。。。一个对象创建多个线程
  Thread t2 = new Thread(r,"线程2");
  Thread t3 = new Thread(r,"线程3");
  Thread t4 = new Thread(r,"线程4");
  Thread t5 = new Thread(r,"线程5");
  Thread t6 = new Thread(r,"线程6");
  t1.start();
  t2.start();
  t3.start();
  t4.start();
  t5.start();
  t6.start();
  
  
 }
}

3.

package lianxi.Runnable;

public class runnableTest3 implements Runnable{
 private String name;
 private String role;
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getRole() {
  return role;
 }
 public void setRole(String role) {
  this.role = role;
 }
 public runnableTest3() {
  super();
 }
 public runnableTest3(String name, String role) {
  super();
  this.name = name;
  this.role = role;
 }
 /** 当for循环中的内容不止一句的时候,由于线程的时间片轮转机制,经常会遇到当前线程还没有完成一个完整的for循环,
  *   就转换成其他的线程开始执行这个方法了。也就是说我的这个线程刚执行一半,就轮到其他线程了,为了解决这个问题,就
  *   出现了线程同步的概念,线程同步分成两个内容,一是方法同步,一个是代码块同步。提高了线程安全,缺降低了线程的并发性
  * 不管是代码块同步还是方法同步,synchronized加锁(同步)的时候都需要一个对象。
  *
  *  @方法的作用:线程中的同步代码块。代码块同步(加锁),只是本次循环会同步,下次循环就是其他线程执行了。
  *      因此代码块同步的并发性比方法同步要大。synchronized需要一个对象(引用类型的),这里给synchronized
  *      一个字符串类型的变量
  *  @作者:褚永也
  *  @日期:2014-7-17 下午3:08:03
  *  @返回值类型:void
  */
  public  void move(){
     for(int i=0;i<10;i++){
      synchronized ("a") {
       System.out.println(Thread.currentThread().getName()+" 射击第"+i+"发子弹.....");
       System.out.println(Thread.currentThread().getName()+" 第"+i+"发子弹,击中目标.");
   }
     
     }
    }
 
  public void run(){
   this.move();
  }
  public static void main(String[] args) {
   runnableTest3 r1 = new runnableTest3("张三","土匪");
   runnableTest3 r2 = new runnableTest3("李四","警察");
   runnableTest3 r3 = new runnableTest3("王五","警察");
   runnableTest3 r4 = new runnableTest3("竹柳","警察");
   runnableTest3 r5 = new runnableTest3("小明","土匪");
   Thread th1 = new Thread(r1);//这里调用的是线程Thread的带一个参数的构造方法
   Thread th2 = new Thread(r2);
   Thread th3 = new Thread(r3);
   Thread th4 = new Thread(r4);
   Thread th5 = new Thread(r5);
   th1.start();
   th2.start();
   th3.start();
   th4.start();
   th5.start();
   
 }
}

4.

package lianxi.threadTest;
/**
 * 以继承Thread类的方式实现线程
 *
 */
public class ThreadTest extends Thread{
 
 private String name;//姓名
 private String role;//角色
 public ThreadTest(String name, String role) {
  super();//构造函数默认情况下调用了父类的无参构造函数
  this.name = name;
  this.role = role;
 }
   public void move(){
    for(int i=0;i<10;i++){
     System.out.println(this.name+"移动了"+i+"米");
    }
   }
    @Override
 public void run() {
  //super.run();
  move();
  try {
   sleep(100000);
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
   public static void main(String[] args) {
    ThreadTest  t1 = new ThreadTest("小强","土匪");
    ThreadTest  t2 = new ThreadTest("小时","土匪");
    ThreadTest  t3 = new ThreadTest("小米","警察");
    //启动线程
    t1.start();//构造函数默认情况下调用了父类(线程)的无参构造函数
    t2.start();
    t3.start();
 }

}

5.卖票过程

package lianxi.tickets;


/**
 * 1.Tickets extends Thread
 * 2.Tickets implements Runnable
 * 在使用线程做题的时候首先要考虑用上面的两种方式的哪一种。
 * 由于java中类的单继承多实现的,因此一般采用实现接口的方法。因为类如果继承了Thread以后就不能继承其他类了。
 *
 */
public class Tickets implements Runnable{

 int ticket=100;//票数
 
 /**1.重写接口中抽象的方法
  * 2.为了线程安全都会用到synchronized同步。
  * 3.当同步方法的时候,结果只能是一个窗口售票100张,因此只能用同步代码块的方法,把synchronized写在方法里
  * 4.当synchronized写在while外面的时候,其实效果跟同步方法一样了,因为synchronized控制了整个方法体,那么synchronized只能写在while循环里
  * 5.synchronized写在循环里,可是当一个进程正在执行synchronized里面的第一句话的时候,其他线程可能会去执行while(ticket>0),因为
  * while(ticket>0)这行代码并没被加锁(synchronized)。。这样导致的结果就是在最后一张票的时候会四个窗口都卖。(因为一个窗口在卖票的时候其他在三个窗口
  * 已经执行完了while(ticket>0)这行代码,这就导致了多买了三张票(0,-1,-2)
  * 6.解决这个问题,给需要执行的语句加上限制条件,if(ticket>0)
  */
 public void run() {
  while(ticket>0){
   synchronized ("ssss") { 
    if(ticket>0){
           System.out.println(Thread.currentThread().getName()+"卖出第"+ticket+"票");//synchronized里面的第一句话的时候
           ticket--;        
   }
   }
  } 
 }
 public static void main(String[] args) {
      Tickets t = new Tickets();//创建一个对象
  Thread t1 =new Thread(t,"seller1");//用这个对象封装四个线程(调用Thread两个参数的构造函数),代表四个窗口
  Thread t2 =new Thread(t,"seller2");
  Thread t3 =new Thread(t,"seller3");
  Thread t4 =new Thread(t,"seller4");
  t1.start();//开启线程,默认调用run()方法
  t2.start();
  t3.start();
  t4.start();
 }

}

6.转账过程

package com.bank;


public class Bank {//deposit存款  transfer转账
 static int money=100;
 
 
 public synchronized static void deposit(int am){
   money=money+am;
  System.out.println("存款成功,余额为:"+money);
 }
 public synchronized static void transfer(int bm){
  if(money>=bm){
    money=money-bm;
   System.out.println("转账成功,余额为:"+money);
  }else{
   System.out.println("余额不足");
  }
 }


 

}

package com.bank;
/**
 *
 *两个人都能操作一个银行卡
 *
 */
public class Customer extends Thread{
 public  void run(){
  System.out.println(Thread.currentThread().getName());
  Bank.deposit(100);
  System.out.println(Thread.currentThread().getName());
     Bank.transfer(120);
 }
 public static void main(String[] args) {
  Customer c = new Customer();
  Thread t1=new Thread(c,"用户1");
  Thread t2=new Thread(c,"用户2");
  t1.start();
  t2.start();
 }

}

猜你喜欢

转载自1601844782.iteye.com/blog/2271520