毕向东之线程

/*进程(如任务管理器):是一个正在执行中的程序,每一个进程执行都有一个执行顺序。
 线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行。并且一个进程中至少有一个线程。
 
 Java VM 启动的时候会有一个进程java.exe.该进程中至少有一个线程负责java程序的执行
 而且这个线程运行的代码存在于main方法中。该线程称为主线程
 扩展:如果更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程
 
创建线程的第一种方式:继承Thread类
步骤:1.定义类继承Thread 2.复写Thread类中的run方法 (目的:将自定义代码存储在run方法中,让线程运行)
    3.调用线程的static方法(该方法作用:启动线程,调用run方法)
  
发现运行结果每一次都不同。因为多个线程都获取cpu的执行权,cpu执行到谁,就运行谁。在某一个时刻,只能有一个
程序在运行(多核除外) cpu在做着快速的切换,以达到看上去是同时运行的效果。
多线程的特性:随机性,随抢到执行权就执行谁
 */
public class Test1 {

    public static void main(String[] args) {
        Demo d=new Demo();//创建线程
        d.start();//开启线程 执行run方法 
        //d.run();这样仅仅是对象调用方法。线程创建了,并没有运行
        for(int x=0;x<60;x++){
            System.out.println("====主线程"+x);
        }
    }
}
class Demo extends Thread{
    public void run(){
        for(int x=0;x<60;x++){
            System.out.println("线程"+x);
        }
    }
}

 

 

/*1.线程创建 2.运行(既有运行权又有运行资格):start(); 3.消亡:1).stop(); 2).run方法结束
 3.冻结(放弃了执行资格):包括sleep(time);wait();notify()唤醒(唤醒后有运行资格,不一定有执行权)
 
 线程都有自己的名称,默认为:Thread-编号。编号从0开始       调用父类的构造方法就可以改变默认的名称
Thread类的静态方法: currentThread();获取当前线程对象
getName(); 获取线程名称。

设置线程名称:setName或者构造函数
 */
public class Test2 {

    public static void main(String[] args) {
        Test t1=new Test("one---");
        Test t2=new Test("two+++");
        t1.start();
        t2.start();
        for(int x=0;x<10;x++){
            System.out.println("=====主线程:"+x);
        }//三个线程都执行完程序结束
    }
}
class Test extends Thread{
    public Test(String name){
        super(name);//将自定义name传给父类的构造方法
    }
    public void run(){
        for(int x=0;x<10;x++){
            System.out.println(Thread.currentThread().getName()+"==:"+x);
        }
    }
}

 

 

/*创建线程的第二个方法:
 步骤:1.定义类实现Runnable接口。(避免了单继承的局限性)
 2.覆盖Runnable中的Run方法(将线程需要执行的代码放run方法中)
 3.通过Thread类建立线程对象
 4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
 5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
 
 两种方式的区别:
 继承Thread:线程代码存放在Thread子类run方法中。
 实现Runnable:线程代码存在接口的子类的run方法中。
 
多线程的运行出现安全问题。
问题的原因:当多条语句在操作同一个线程共享时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与进来。
java对于多线程的安全问题提供了专业的解决方式:就是同步代码块:
                                synchronized(对象){
                                    需要同步的代码块;
                                }
对象如同锁。持有锁的线程可以在同步中执行,没有执行锁的线程即使获取了cpu的执行权,也进不去,因为没有获取锁。

同步的前提:
1.必须要有两个或者两个以上的线程
2.必须是多个线程使用同一个锁
必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源,
 */
public class Test3 {

    public static void main(String[] args) {
        Demo3 d=new Demo3();
        Thread d1=new Thread(d);//创建线程,执行d的run方法
        Thread d2=new Thread(d);
        d1.start();
        d2.start();//两个线程都实现d的run方法
        
    }
}
class Demo3 implements Runnable{
    private static int tick=100;
    Object obj=new Object();
    public void run(){
        while(true){
            synchronized(obj){//同步里面一对象锁
                if(tick>0){//当在这里判断后执行权被抢走时,就会出现数据的错乱。就需要用同步来解决
                    try{
                        Thread.sleep(10);
                    }catch(Exception e){}//睡眠等待异常
                    System.out.println(Thread.currentThread().getName()+"sale:"+tick--);//当前对象的getName();
                }
            }
        }
    }
}

 

 

/*两个线程运行同一个对象的run方法
 同步函数使用的锁是this
 如果同步函数被静态修饰后,使用的锁是类名。类名.class
因为静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件的对象(类名.class)
 */
public class Test4 {

    public static void main(String[] args) {
        Cus c=new Cus();
        Thread t1=new Thread(c);
        Thread t2=new Thread(c);
        t1.start();
        t2.start();//都运行c的run方法
    }
}
class Bank{
    private int sum;
    //Object obj=new Object();
    public synchronized void add(int n){//同步函数
        //synchronized (obj){   同步代码块
            sum=sum+n;
            try{
                Thread.sleep(10);
            }catch(Exception e){}
            System.out.println(Thread.currentThread().getName()+"sum="+sum);            
        //}
    }
}
class Cus implements Runnable{
    private Bank b=new Bank();
    public void run(){ //实现父类 不能抛
        for(int x=0;x<3;x++){
            b.add(100);
        }
    }
}

 

 

/*
 对于多个生产者与消费者,为什么要定义while判断标记?
 原因:让被唤醒的线程再次判断标记。
 为什么要定义notifyAll?
 因为需要唤醒对方线程,如果只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。
 */
/*==========================================================================
 public class Test5 {
    public static void main(String[] args) {
        Resource r=new Resource();
        Producer pro=new Producer(r);
        Consumer con=new Consumer(r);
        Thread t1=new Thread(pro);
        Thread t2=new Thread(pro);
        Thread t3=new Thread(con);
        Thread t4=new Thread(con);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class Resource{
    private String name;
    private int count=1;
    private boolean flag=false;
    public synchronized void set(String name){
        while(flag)//因为被等待的线程醒过来后,会继续往下执行代码,如果此处为if,就不会判断flag标记,造成数据出错
            try{wait();}catch(Exception e){  }
        this.name=name+"--"+count++;
        System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
        flag=true;
        this.notifyAll();
    }
    public synchronized void out(){
        while(!flag)
            try{wait();}catch(Exception e){  }
        System.out.println(Thread.currentThread().getName()+"....消费者......."+this.name);
        flag=false;
        this.notifyAll();
    }
}
class Producer implements Runnable{
    private Resource res;
    public Producer(Resource res){
        this.res=res;
    }
    public void run(){
        while(true){
            res.set("+商品+");
        }
    }
}
class Consumer implements Runnable{
    private Resource res;
    public Consumer(Resource res){
        this.res=res;
    }
    public void run(){
        while(true){
            res.out();
        }
    }
}
=======================================================================================*/

 

 

/*
 jdk5.0后:Lock替代了synchronized, Condition替代了Object 就避免了while,notifyAll的麻烦
 */
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test6 {
    public static void main(String[] args) {
        Resource r=new Resource();
        Producer pro=new Producer(r);
        Consumer con=new Consumer(r);
        Thread t1=new Thread(pro);
        Thread t2=new Thread(pro);
        Thread t3=new Thread(con);
        Thread t4=new Thread(con);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
/*如果t3、t4抢到执行权,遇到flag则等待,假设t1先抢到执行权:生产一个,改变标记,唤醒对方并释放锁;如果t1或者t2抢到执行权则等待;
  再假设t3抢到执行权,得到锁,则消费一个,改变标签,唤醒对方并释放锁;如果t3或者t4抢到执行权则等待;被唤醒的t1或t2中的一个则生产一个....
  
同步函数与lock的区别:当线程被等待后,该函数的功能则结束,同步也就结束,但得到lock后,被等待函数也结束,但是没释放锁,所以释放锁必须得放到finally里  */
class Resource{
    private String name;
    private int count=1;
    private boolean flag=false;
    private Lock lock=new ReentrantLock();//创建一锁 并且一个锁可以绑定好几个对象
    private Condition condition_pro=lock.newCondition();//返回绑定到Lock实例的新Condition实例
    private Condition condition_con=lock.newCondition();//不同对象的锁
    public void set(String name){
        lock.lock();//得到锁
            try{
                while(flag)//因为被等待的线程醒过来后,会继续往下执行代码,如果此处为if,就不会判断flag标记,造成数据出错
                condition_pro.await();//生产者等待
                this.name=name+"--"+count++;
                System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
                flag=true;
                condition_con.signal();//唤醒某一个消费者 实现了本方只唤醒对方操作
            }catch(Exception e){  }        
            finally{
                lock.unlock();//释放锁  必须被执行            
            }
    }
    public synchronized void out(){
        lock.lock();        
            try{
                while(!flag)
                condition_con.await();
                System.out.println(Thread.currentThread().getName()+"....消费者......."+this.name);
                flag=false;
                condition_pro.signal();//避免唤醒了本方线程
            }catch(Exception e){  }
        finally{
            lock.unlock();//释放锁              
        }
    }
}
class Producer implements Runnable{
    private Resource res;
    public Producer(Resource res){
        this.res=res;
    }
    public void run(){
        while(true){//不搞循环就没必要多线程,但要避免死循环(下例为解决方法)
            res.set("+商品+");
        }
    }
}
class Consumer implements Runnable{
    private Resource res;
    public Consumer(Resource res){
        this.res=res;
    }
    public void run(){
        while(true){
            res.out();
        }
    }
}

 

 

/*stop方法已经过时,如何停止线程呢?只有一种方法,让run方法结束
 开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束
 特殊情况:当线程在执行改变标记之前时被冻结了,线程就不会改变标记,就不会结束线程。所以就有了interrupt(会产生中断异常)。
 interrupt就是强制将冻结的线程恢复到运行状态中(还可以清除主线程状态,避免主线程被冻结后程序停着)
 
 守护线程:守护主线程,主线程结束后,守护线程将自动结束。
 格式:t1.setDaemon(true); 在开启线程之前设定为守护线程
 */
public class Test7 {
    public static void main(String[] args) {
        StopThread st=new StopThread();
        Thread t1=new Thread(st);
        Thread t2=new Thread(st);
        t1.start();
        t2.start();
        int num=0;
        while(true){
            if(num++==60){
                t1.interrupt();
                t2.interrupt();
                //st.changeFlag();在改变之前就已经停止线程了
                break;
            }
            System.out.println(Thread.currentThread().getName()+"...."+num);//主线程名为main
        }
    }
}
class StopThread implements Runnable{
    private boolean flag=true;
    public synchronized void run(){
        while(flag){
            try{
                wait();
            }catch(InterruptedException e){
                flag=false;//强制弄醒后改变标志,停止运行
                System.out.println(Thread.currentThread().getName()+"...Exception");
            }
            System.out.println(Thread.currentThread().getName()+"...run");
        }
    }
    public void changeFlag(){
        flag=false;
    }
}

 守护线程例子:

 @org.junit.Test
    public void addition_isCorrect() {
        assertEquals(4, 2 + 2);

        Thread t1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 500; i++) {
                    System.out.println("..............i = " + i);
                }
            }
        };

        Thread t2 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 2; i++) {
                    System.out.println("..2");
                }
            }
        };


        t1.setDaemon(true);//true 代表将t1设置为守护线程
        t1.start();
        
        t2.start();//t2结束后  t1就自动结束(但是结束需要些时间)

    }

日志:

..2
..............i = 0
..............i = 1
..............i = 2
..............i = 3
..2
..............i = 4
..............i = 5
..............i = 6
..............i = 7
..............i = 8
..............i = 9
..............i = 10
..............i = 11
..............i = 12
..............i = 13
..............i = 14
..............i = 15
..............i = 16
..............i = 17
..............i = 18
..............i = 19
..............i = 20
..............i = 21
..............i = 22
..............i = 23
..............i = 24
..............i = 25
..............i = 26
..............i = 27
..............i = 28
..............i = 29
..............i = 30
..............i = 31
..............i = 32
..............i = 33
..............i = 34
..............i = 35
..............i = 36
..............i = 37
..............i = 38
..............i = 39
..............i = 40
..............i = 41
..............i = 42
..............i = 43
..............i = 44
..............i = 45
..............i = 46
..............i = 47
..............i = 48
..............i = 49
..............i = 50
..............i = 51
View Code

 

 

/*join:
 当A线程执行到了B线程的.join()方法时,A就回等待,等B线程执行完,A线程才会执行。
 join可以用来临时加入线程执行
 
 Thread.yield();//暂停当前线程
 
 t1.setPriority(Thread.MAX_PRIORITY);将t1设置为最低优先级10 默认为5 最高为1
 */
public class Test8 {
    public static void main(String[] args)throws Exception {
        Demo8 d=new Demo8();
        Thread t1=new Thread(d);
        Thread t2=new Thread(d);
        t1.start();        
        t2.start();
        t1.join();//代表t1抢到主线程的执行权,主线程没得执行权被处于冻结状态,等t1执行完所有程序,主线程才活过来
        for(int x=0;x<80;x++){
            System.out.println("main...."+x);
        }
        System.out.println("over");
    }
}
class Demo8 implements Runnable{
    public void run(){
        for(int x=0;x<70;x++){
            System.out.println(Thread.currentThread().getName()+"..."+x);
        }
    }
}

 

锁与wait()的使用:

/**
     * 轮流打印 1  2
     */
    public static class OutputThread implements Runnable {

        private int num;
        private Object lock;

        public OutputThread(int num, Object lock) {
            super();
            this.num = num;
            this.lock = lock;
        }

        public void run() {
            try {
                while(true){
                    synchronized(lock){
                        lock.notifyAll();//唤醒被该锁 锁住的线程
                        lock.wait();//当前线程等待  需要使用lock对象唤醒后才能继续执行

                        Thread.currentThread().sleep(1000);
                        System.out.println(Thread.currentThread().getName() + "," + num);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        public static void main(String[] args){
            final Object lock = new Object();

            Thread thread1 = new Thread(new OutputThread(1,lock),"线程1");
            Thread thread2 = new Thread(new OutputThread(1, lock),"线程2");

            thread1.start();
            thread2.start();
        }

    }

 

posted @ 2016-05-31 12:47  ts-android  阅读(224)  评论(0编辑  收藏  举报