多线程(二)

线程安全

当多线程对共享资源同时曹组时,可能会发生数据的脏读、乱读现象,最终导致数据紊乱,是在多线程编程时必须要避免的问题。

实现线程同步操作

同步:要求多线程依次执行,当第一个线程在操作共享资源(执行一段代码),其他线程处于等待状态,到第一个线程处理完毕,其他线程中,获得CPU分配时间的那一个则可以操作共享资源(执行代码)
异步:多线程互相操作,互不干扰。
synchronize:同步操作
关键词的操作有三种方式:
1、作用在普通方法上
表示同一时间,只能有一个线程在执行该方法,等到此线程执行结束,获得CPU分配时间的那一个则可以操作共享资源(执行代码)

public class ThreadDemo01 {

    public static void main(String[] args) {
        final Table table = new Table();
        //电脑1的桌子
        Thread thread1 = new Thread(){
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //欢乐斗地主
                    String name = 
                        Thread.currentThread().getName();
                    System.out.println(name+" 玩了一局欢乐豆,还剩:"+table.playGame());
                }
            }
        };
        //电脑2的桌子
        Thread thread2 = new Thread(){
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //欢乐斗地主
                    String name = 
                        Thread.currentThread().getName();
                    System.out.println(name+" 玩了一局欢乐豆,还剩:"+table.playGame());
                }
            }
        };

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

}
class Table{
    int beans = 1000;//系统第一次赠送1000个欢乐豆
    
    public synchronized int playGame(){
        if(beans <= 0){
            //玩完了,game over。
            throw new RuntimeException("欢乐豆不足,请充值...");
        }
        return beans -= 100;//每玩一局,系统扣除100欢乐豆
    }
}

2、作用在代码块上(最常用)
如果仅对代码块实现同步效果,目的就是提高代码的多线程操作,缩小需要同步的范围,目的就是提高代码的运行效率
实现语法:
synchronize(共享的对象){
需要同步的代码块
}

共享对象: this , 静态OBject全局变量

public class ThreadDemo02 {
public static void main(String[] args) {
        final Shop youYiKu = new Shop();
        //客户1
        Thread customer1 = new Thread(){
            @Override
            public void run() {
                youYiKu.shopping(Thread.currentThread().getName());
            }
        };
        //客户2
        Thread customer2 = new Thread(){
            @Override
            public void run() {
                youYiKu.shopping(Thread.currentThread().getName());
            }
        };

        customer1.start();
        customer2.start();  
}
}
 * 模拟客户进店挑选、试衣、结账离开效果。
class Shop{
    public /*synchronized*/ void shopping(String peopleName){
        try {
            System.out.println(peopleName+"进店了....");
            System.out.println(peopleName+"正在挑选衣服...");
            Thread.sleep(5000);
            /*
             * 此优衣库只有一个试衣间
             */
            synchronized(this){
                System.out.println(peopleName+"挑选完毕,正在试衣...");
                Thread.sleep(5000);
            }
            System.out.println(peopleName+"试衣完毕,结账离开。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3、作用在静态方法上(不常用)
多线程共享操作的数据,属于类的,与对象无关,无论多少个对象,数据只有一份,多线程对静态数据操作,也可能会发生数据脏读、乱读现象,所以可以对静态方法实现同步。
结论:
同步(加锁)范围,适当就好
多线程的使用就是为了提高代码执行效率
不恰当的对代码、方法惊醒加锁,反而降低了代码的执行效率,不可取
所以同步范围决定着多线程代码的执行效率

线程中的五中状态
新建 new,待执行runnable,执行running,阻塞blocking,死亡dead

Object 中的其他方法

wait,notify,notifyAll
当需要某个进程后台执行,不影响主线程时,可以考虑用以下程序

模拟垃圾百度全家桶

public class ObjectMethodDemo04 extends Object{
public static void main(String[] args) {
        final Object obj = new Object();
        /*
         * 搜狗输入法下载线程
         */
        final Thread souGouDownLoad = new Thread(){
            @Override
            public void run() {
                System.out.println("搜狗输入法下载 start...");
                for(int i=1; i<=100; i++){
                    System.out.println("下载了 "+i+"%");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("搜狗输入法下载 end...");
                
                /*
                 * 要求搜狗输入法,在下载完毕时,就安装
                 * 百度安全认证的下载安装,是后台执行或者是
                 * 在搜狗输入法安装之后才执行。
                 */
                //要对共享的Object obj对象实现wait的线程唤醒操作
                synchronized(obj){
//                  obj.notify();//唤醒被当前Object对象阻塞的线程。
                    obj.notifyAll();//唤醒被当前Object对阻塞的所有的线程。
                }
                
                /*
                 * 百度安全认证流氓下载、安装。
                 */
                System.out.println("百度安全认证下载 start...");
                for(int i=1; i<=100; i++){
                    System.out.println("下载了 "+i+"%");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("百度安全认证下载 end...");
                
                System.out.println("百度安全认证安装 start...");
                for(int i=1; i<=100; i++){
                    System.out.println("安装了 "+i+"%");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("百度安全认证安装 end...");
            }
        };
        
        /*
         * 搜狗输入法安装线程
         */
        Thread souGouFixed = new Thread(){
            @Override
            public void run() {
                System.out.println("搜狗输入法安装 start...");
                
                try {
                    synchronized(obj){
                        //先下载,才能安装
                        souGouDownLoad.start();
                                              souGouDownLoad.join();
                                              obj.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                for(int i=1; i<=100; i++){
                    System.out.println("安装了 "+i+"%");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("搜狗输入法安装 end...");
            }
        };
        
        souGouFixed.start();
                
    }

线程池

  • 利用有效的线程数量,处理更多的任务。
  • 有效的控制了内存的不必要消耗,提高服务器的性能。
  • 线程池:限制线程数量的。
  •       有规律的处理更多的任务。
    
public class ThreadPoolDemo05 {
public static void main(String[] args) {
        ExecutorService threadPool = 
                Executors.newFixedThreadPool(2);
        Runnable runnable = null;
        for(int i=0; i<5; i++){
            final int number = i+1;
            runnable = new Runnable(){
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+
                                            "在执行任务"+number);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }; 
       Thread thread = new Thread(runnable);
* 将任务交给线程池来管理,
 *   线程池会分配不同的线程去执行任务。
 */
            threadPool.execute(runnable);
        }
        
        //将线程池关闭
        threadPool.shutdown();
        
    }
}

将不安全的集合线程转换为安全的集合线程

public class SynchronizedCollectionDemo05 {

    public static void main(String[] args) {
        /*
         * 线程非安全的List
         *      → 线程安全的List集合 
         * 
         * 通过Colleactions工具类中的集合同步方法
         */
        List list = new ArrayList();
        //非安全的list → 线程安全的list
        list = Collections.synchronizedList(list);
        
        /*
         * 线程非安全的Set
         *      → 线程安全的Set集合 
         * 
         * 通过Colleactions工具类中的集合同步方法
         */
        Set set = new HashSet();
        //非安全的set → 线程安全的set
        set = Collections.synchronizedSet(set);
        
        /*
         * 线程非安全的Map
         *      → 线程安全的Map集合 
         * 
         * 通过Colleactions工具类中的集合同步方法
         */
        Map map = new HashMap();
        //非安全的map → 线程安全的map
        map = Collections.synchronizedMap(map);

    }

}

你可能感兴趣的:(多线程(二))