线程的基本了解

1、线程的实现方式

线程有两种实现方式,分别为继承Thread、实现Runnable接口。差异性:实现方式避免了类的继承单一性,且对于多个线程同时访问同一个资源时更便捷。

(1)继承Thread

class TestThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public class TestThreadMethod {
    public static void main(String[] args) {
        TestThread th1 = new TestThread();
        th1.setName("testThread");
        th1.start();
    }
}
View Code

(2)实现Runnable接口

class TestRunnable implements Runnable{
    @Override
    public void run() {
        for (int i=1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

public class TestMain {
    public static void main(String[] args) {
        TestRunnable tr = new TestRunnable();
        Thread th1 = new Thread(tr, "th1");
        Thread th2 = new Thread(tr, "th2");
        th1.start();
        th2.start();
    }
}
View Code

(3)线程的生命周期

2、线程的常用方法

start():线程的开启。同一个线程在结束时只能开启一次,否则报错。

setName():设置当前线程的名称。

getName():获取当前线程的名称。

currentThread():获取当前线程的对象。

priority():线程的级别。静态值有,0/5(默认)/10。值越高,代表争夺CPU的使用权的几率越大,但不是绝对。

yield():线程让步。当前线程A调用yield()方法时,释放CPU的使用权,重新与其他线程B争夺CPU的使用权。

join():线程加入。在A线程中,调用B线程的join方法时,A线程的程序等待B线程的程序执行完才能继续执行A线程。

sleep():线程睡眠。当线程执行该方法时,进入设置的睡眠时间后继续执行程序。

isAlive():线程是否在活动,返回值为boolean。

wait():线程等待。必须在同步方法中使用。

notify()/notifyAll():线程唤醒,与wait()结合使用,用于线程通信。

注意:(1)线程在同步时,wait()会释放锁,而join()与sleep()方法不会释放锁。

   (2)wait()、notify()、notifyAll()严格来说不算线程中的方法,这三个方法是定义在Object对象中。

class TestThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public class TestThreadMethod {
    public static void main(String[] args) {
        TestThread th1 = new TestThread();
        th1.setName("testThread");
        th1.setPriority(Thread.MAX_PRIORITY);
        th1.start();
        
        Thread.currentThread().setName("=======mainThread");
        for (int i = 0; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            if (i % 10 == 0) {
                Thread.yield();
            }
            if (i == 20) {
                try {
                    th1.join();
                    System.out.println(th1.isAlive());
                    th1.start();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
View Code

3、线程的同步

线程的同步分为两种:同步代码块和同步方法块。

class TestThread extends Thread {
    static int ticket = 100;
    static Object obj = new Object();
    
    @Override
    public void run() {
        /*1、同步代码块时,只能同步需要的代码块,其余无关代码块不行,会影响同步;
         * 2、同步监视器用this,代表当前对象,此时继承方式,对象是多个,所以无法同步;
         * 此两种情况出现重复输出情况,如下的this情况
         */
        /*synchronized (this) {
            while(true) {
                    try {
                        Thread.currentThread().sleep(100);
                    } catch (InterruptedException e) {
                        // TODaO Auto-generated catch block
                        e.printStackTrace();
                    }
                    if (ticket <= 0) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "窗口:" +ticket--);
                }
            }*/
            while(true) {
                synchronized (obj) {//obj充当锁时必须是静态的,否则多个对象代表的锁是各自的。
                    try {
                        Thread.currentThread().sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (ticket <= 0) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "窗口:" +ticket--);
                }
            }
    }
}

public class TestMain {
    public static void main(String[] args) {
        TestThread th1 = new TestThread();
        TestThread th2 = new TestThread();
        th1.setName("th1");
        th2.setName("th2");
        th1.start();
        th2.start();
    }
}
View Code
class TestThread implements Runnable {
    int ticket = 100;
    @Override
    public void run() {
        while(true) {
            importNum();
        }
    }
    /**
     * 同步方法块时,同步锁为当前对象.所以方法的类必须是同一个对象
     */
    private synchronized void importNum() {
        if (ticket <= 0) {
            return;
        }
        System.out.println(Thread.currentThread().getName() + "窗口:" + ticket--);
    }
}

public class TestMain {
    public static void main(String[] args) {
        TestThread th = new TestThread();
        Thread th1 = new Thread(th, "th1");
        Thread th2 = new Thread(th, "th2");
        
        th1.start();
        th2.start();
    }
}
View Code

注:同步在Thread与Runnable两种方式的实现中有很大的区别,详见上例。

拓展示例:

class SingleTon {
    private SingleTon() {
        
    }
    public static SingleTon instance = null;
    
    public static SingleTon getSingleTon() {
        /**
         *     原程序直接判断,创建实例,返回。在多线程中,存在线程安全问题,会创建多个SingleTon;
         * 开发中为避免这情况发生,采用下面同步的实现方式。
         */
        /**
            if (instance == null) {
                instance = new SingleTon();
            }
            return instance;
        */
        if (instance == null) {//提高同步效率,后面访问方法的对象,无需再次等待
            synchronized(SingleTon.class) {//采用类本身的对象来充当锁
                if (instance == null) {
                    instance = new SingleTon();
                }
            }
        }
        return instance;
    }
}

public class TestMain {
    public static void main(String[] args) {
        
        SingleTon st1 = SingleTon.getSingleTon();
        SingleTon st2 = SingleTon.getSingleTon();
    }
}
View Code

4、线程的死锁

死锁:当两个线程同时需要对方手中的锁时,各自都在等对方放弃手中的锁,而使程序无限期的等待下去,称为死锁。

public class TestMain {
    public static void main(String[] args) {
        
        StringBuffer sb1 = new StringBuffer();
        StringBuffer sb2 = new StringBuffer();
        
        new Thread(){
            public void run() {
                
                synchronized(sb1) {
                    sb1.append("A");
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized(sb2) {
                        sb2.append("B");
                        System.out.println("sb1:" + sb1.toString());
                        System.out.println("sb2:" + sb2.toString());
                    }
                }
                
            };
        }.start();
        
        new Thread(){
            public void run() {
                synchronized(sb2) {
                    sb2.append("C");
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized(sb1) {
                        sb1.append("D");
                        System.out.println("sb1:" + sb1.toString());
                        System.out.println("sb2:" + sb2.toString());
                    }
                }
            };
        }.start();
    }
}
View Code

上述例子中,第一个线程执行第一个同步块代码后,进行睡眠,握住sb1的锁;此时第二个线程可能已经开启运行,获取sb2的锁,进行同步代码块方法,进行睡眠。之后第一个线程睡眠时间过后要进行第二个同步代码块的执行,这时sb2的锁握在了第二个线程,而第二个线程在醒来后要获取第二块同步方法的锁时,这时的锁在第一个线程中。两个线程同时在等待对方释放手中的锁,导致了死锁的出现。

5、线程的通信

/**
 * 生产者 --> 店员(数量最多存放20) --> 消费者
 *
 */

class Clerk {
    int num;
    public synchronized void addProduct() {
        if (num >= 20) {
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        notifyAll();
        num++;
        
        System.out.println(Thread.currentThread().getName() + ":" + num);
    }
    public synchronized void consume() {
        if (num == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        notifyAll();
        System.out.println(Thread.currentThread().getName() + ":" + num);
        num--;
    }
}

class Customer implements Runnable {
    Clerk clerk;
    
    public Customer(Clerk clerk) {
        this.clerk = clerk;
    }
    
    public void run() {
        while(true) {
            try {
                Thread.currentThread().sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consume();
        }
    }
}

class Producer implements Runnable {
    Clerk clerk;
    
    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }
    
    public void run() {
        while(true) {
            try {
                Thread.currentThread().sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.addProduct();
        }
    }
}

public class TestMain {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Customer c1 = new Customer(clerk);
        Producer p1 = new Producer(clerk);
        Thread th2 = new Thread(p1, "生产者");
        Thread th1 = new Thread(c1, "消费者");
        th2.start();
        th1.start();
    }
}
View Code

 

 

 

 

posted @ 2018-08-20 22:43  漫步云端~  阅读(346)  评论(0编辑  收藏  举报