线程的概念
在一个程序中同时独立运行的多个独立流程,每一个独立流程就是一个线程

 
线程的三要素
CPU,Code,Data

 
进程:完成一个任务的过程

 
一个进程包括多个线程
线程是计算机执行的最小程序单元

 
线程并发

 

 
线程的开发:继承Thread类与实现Runnable接口2种方式

 
1.继承Thread类:
public class MyThread1 extends Thread {
       public void run(){
             for(int i=0;i<60;i++){
                  System. out.println("####" +i);
            }
      }

}

public class MyThread2 extends Thread{
       public void run(){
             for(int i=0;i<60;i++){
                  System. out.println("$$$$" +i);
            }
      }

}

public class TestThread01 {
       public static void main(String[] args) {
            Thread t1= new MyThread1();
            Thread t2= new MyThread2();
            t1.start();
            t2.start();
            
      }


}

2.实现Runnable接口
  用户开发一个类实现Ruannable接口
  实现run()方法
  运行线程
Ruannable target=new MyRunnable2();
Thread t3=new Thread(target);
public class MyThread03 implements Runnable{

       public void run() {
             for(int i=0;i<400;i++){
                  System. out.println("****" +i);
            }
            
      }

}

public class MyThread04 implements Runnable{

       public void run() {
             for(int i=0;i<400;i++){
                  System. out.println("&&&&" +i);
            }
            
      }

}

public class TestThread3 {

      
       public static void main(String[] args) {
            Runnable runn3=new MyThread03();
            Runnable runn4= new MyThread04();
            Thread t3= new Thread(runn3);
            Thread t4= new Thread(runn4);
            t3.start();
            t4.start();
      }

}







继承Thread是面向对象的编程方式
实现Runnable接口解决了单一继承限制

 

 

 
线程的状态
初始状态
Thread a=new Thread();
可运行状态
a.start(),但此时未获得CPU或者可运行状态CPU时间片到期
运行状态
可运行状态获得CPU
终结状态
run()方法退出

 

 
sleep()方法
public static void sleep(long millis) throws InterruptedException

 

 
public class MyThread1 extends Thread {
       public void run(){
             for(int i=0;i<10;i++){
                  System. out.println("####" +i);
                   try {
                        Thread. sleep(200);
                  } catch (InterruptedException e) {
                        
                  }
            }
      }

}

public class MyThread2 extends Thread{
       public void run(){
             for(int i=0;i<10;i++){
                  System. out.println("$$$$" +i);
                   try {
                        Thread. sleep(200);
                  } catch (InterruptedException e) {
                        
                  }
            }
      }

}

public class TestThread01 {
       public static void main(String[] args) {
            Thread t1= new MyThread1();
            Thread t2= new MyThread2();
            t1.start();
            t2.start();
            
      }


}

运行结果:
$$$$0
####0
####1
$$$$1
$$$$2
####2
$$$$3
####3
####4
$$$$4
$$$$5
####5
$$$$6
####6
####7
$$$$7
####8
$$$$8
$$$$9
####9


join()方法:
  join()方法也会导致线程的阻塞
  特点:如果当前线程中调用了另外一个线程join方法,
 

 
join()方法的问题:
  如果两个线程彼此调用对方的join方法,会导致程序无法运行
  解决方法如下:
    public final void join(long millis) throws InterruptedException

public class MyThread2 extends Thread{
      Thread t;
       public void run(){
      
             for(int i=0;i<100;i++){
                  System. out.println("$$$$" +i);
                   try {
                         t.join(200);
                  } catch (InterruptedException e) {
                        e.printStackTrace();
                  }
            
            }
      }

}








线程同步
多个线程并发访问同一个对象,如果破坏了不可分割的操作,从而就会造成数据不一致。

临界资源:多个线程并发访问同一个对象
原子操作:不可分割的操作
被多线程并发访问时,如果一个对象有可能出现数据不一致的问题,那么这个对象就称为线程不安全的对象




如何解决多线程并发访问的问题
synchronized(object){


}
或者写成synchronized(this){}


synchronized修饰方法
public  synchronized void push(){}


死锁
用于解决死锁的方法:wait()与notify()方法(这两种方法也可进行线程间的通信)

 
生产者与消费者问题:
     同时两个线程操作一个栈,一个线程负责往栈中添加元素,一个线程负责删除栈中元素。

 
5.生产者与消费者问题
  1.创建临界资源(Stack) push(),pop(),print()
  2.给push()与pop()方法加上线程同步
  3.定义一个线程类,实现往Stack对象中存放那个相应数据,在线程中初始化临界资源(Stack)
  4.定义一个线程类,实现从Stack中取出相应数据,在线程中初始化临界资源(Stack)
  5.写一个测试类,启动3,4中创建的线程
  6.运行5中的测试类进行测试,发现出现异常,分析异常,解决异常
    异常原因:存数据时,字符数组大小固定导致异常
              取数据时,字符数组下标越界导致异常
  7.解决异常:在一中创建的临界资源(Stack)中的push()方法中加入判断,如果当前下标值==数组长度,则不能继续存入数据,此时,通过线程的wait()方法,通知消费者从临界资源读取数据;
              在一中创建的临界资源(Stack)中的pop()方法中加入判断,如果当前下标值==0,则不能继续取出数据,此时,通过线程的notify()方法,通知生产者向临界资源中存入数据

              当生产者往临界资源中存入数据时,需要通知消费者从临界资源中读取数据;
              当消费者从临界资源中取出数据时,需要通知生产者向临界资源中存入数据;


//临界资源
public class ProducerStack {
private char[] data=new char[12];
private int index=0;
public synchronized void push(char ch){
while(data.length==index){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
data[index]=ch;
/* try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
index++;
this.notify();
System.out.println(ch+" push Stack:");
}
public synchronized void pop(String s){
System.out.println(s);
while(index==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index--;
this.notify();
System.out.println(data[index]+" pop Stack");
data[index]=' ';

}
public void print(String s){
System.out.println(s);
for(int i=0;i<data.length;i++){
System.out.print(data[i]);
}
System.out.println();
}

}
 

//消费者

public class Consumer extends Thread {
 ProducerStack ps;
 public Consumer(ProducerStack ps) {
  this.ps=ps;
 }
 public void run() {
  ps.pop("pop01-----");
  ps.print("pop-------");
 }

}
 

//生产者

public class Producer extends Thread {
 ProducerStack ps;
 public Producer(ProducerStack ps) {
  this.ps=ps;
 }
 
 public void run() {
  for(int i=0;i<20;i++){
   ps.push('s');
   }
  ps.print("push----");
 }

}

 
 
//测试类

public class TestProducer {

 
 public static void main(String[] args) {
  ProducerStack ps=new ProducerStack();
  Thread p=new Producer(ps);
  Thread con=new Consumer(ps);
  p.start();
  con.start();
 }

}