水滴石穿——非一日之功

谈谈多线程

package thread;
/**
 * 线程
 * 线程可以并发执行多段代码,给我们感觉上好像这些代码
 * 在"同时运行"。
 * 
 * 创建线程有两种方式:
 * 方式一:继承Thread并重写run方法。
 * @author ta
 *
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        MyThread2 t2 = new MyThread2();    
        /*
         * 启动线程要调用start方法,而不是直接调用线程
         * 的run方法。
         */
        t1.start();
        t2.start();
    }
}
/**
 * 第一种创建线程的方式存在两个不足之处
 * 1:由于java是单继承的,这导致继承了Thread后就不能再
 *   继承其他类了。在实际开发中经常会继承某个超类来复
 *   用其中的方法,这导致两者不能同时继承。
 *   
 * 2:继承线程后重写run方法来定义任务,这又导致我们将任
 *   务直接定义在线程上,使得线程只能做该任务,无法并发
 *   执行其他任务,重用性变差。  
 * @author ta
 *
 */
class MyThread1 extends Thread{
    public void run() {
        for(int i=0;i<1000;i++) {
            System.out.println("你是谁啊?");
        }
    }
}
class MyThread2 extends Thread{
    public void run() {
        for(int i=0;i<1000;i++) {
            System.out.println("我是查水表的!");
        }
    }
}
package thread;
/**
 * 第二种从创建线程的方式:
 * 实现Runnable接口,单独定义线程任务
 * @author ta
 *
 */
public class ThreadDemo2 {
    public static void main(String[] args) {
        //实例化任务
        Runnable r1 = new MyRunnable1();
        Runnable r2 = new MyRunnable2();
        
        //创建线程
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        
        t1.start();
        t2.start();
        
    }
}

class MyRunnable1 implements Runnable{
    public void run() {
        for(int i=0;i<1000;i++) {
            System.out.println("你是谁啊?");
        }
    }
}
class MyRunnable2 implements Runnable{
    public void run() {
        for(int i=0;i<1000;i++) {
            System.out.println("我是查水表的!");
        }
    }
}

 

线程提供了一系列获取当前线程信息的方法

package thread;
/**
 * 线程提供了一系列获取当前线程信息的方法
 * @author ta
 *
 */
public class ThreadInfoDemo {
    public static void main(String[] args) {
        //获取主线程
        Thread main = Thread.currentThread();
        //获取线程的唯一标识
        long id = main.getId();
        System.out.println("id:"+id);
        
        //获取线程的名字
        String name = main.getName();
        System.out.println("name:"+name);        
        //获取优先级
        int priority = main.getPriority();
        System.out.println("priority:"+priority);        
        //显示是否处于活动状态
        boolean isAlive = main.isAlive();
        //显示是否为守护线程
        boolean isDaemon = main.isDaemon();
        //显示线程是否被中断
        boolean isInterrupted = main.isInterrupted();
        System.out.println("是否活着:"+isAlive);
        System.out.println("是否为守护线程:"+isDaemon);
        System.out.println("是否被中断:"+isInterrupted);
        
        
        
        
    }
}

 

如何创建线程池

package thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程池
 * 线程池是一个管理线程的机制。它主要解决两个问题:
 * 1:重用线程
 * 2:控制线程数量
 * 频繁的创建和销毁线程会给系统带来额外的开销,所以线程应当
 * 得以重用。
 * 当线程数量过多时,会出现资源消耗增大,CPU出现过度切换导致
 * 并发性能降低。对此线程的数量也要得以控制在当前硬件环境所能
 * 承受的范围内。
 * 
 * @author ta
 *
 */
public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool 
            = Executors.newFixedThreadPool(2);
        
        for(int i=0;i<5;i++){
            Runnable r = new Runnable() {
                public void run() {
                    Thread t = Thread.currentThread();
                    System.out.println(t.getName()+":正在执行任务...");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                    }
                    System.out.println(t.getName()+":执行任务完毕...");
                }
            };
            //将任务交给线程池
            threadPool.execute(r);
            System.out.println("将一个任务指派给了线程池!");
        }
        
        
        
//        threadPool.shutdown();//不是立即关闭,只是不再接受新任务
//        threadPool.shutdownNow();//立刻停止,任务没执行完都停
//        System.out.println("线程池停止了!");
    }
}

关于线程安全问题

package thread;
/**
 * 多线程并发的安全问题
 * 当多个线程并发操作同一资源时,由于线程切换时机的不确定
 * 和不可控,会导致操作该资源的代码逻辑执行顺序未按照设计
 * 要求运行,出现了操作混乱。严重时可能导致系统瘫痪。
 * @author ta
 *
 */
public class SyncDemo {
    public static void main(String[] args) {
        final Table table = new Table();
        Thread t1 = new Thread() {
            public void run() {
                while(true) {
                    int bean = table.getBean();
                    Thread.yield();
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                while(true) {
                    int bean = table.getBean();
                    Thread.yield();
                    System.out.println(getName()+":"+bean);
                }
            }
        };
        t1.start();
        t2.start();
    }
}

class Table{
    //桌子上有20个豆子
    private int beans = 20;
    /**
     * 解决并发安全问题的核心就是将多个线程抢着运行改为
     * 有先后顺序的排队运行。
     * Java提供了锁机制,强制多个线程同步运行一个方法
     * 
     * 当一个方法上使用关键字:synchronized修饰后,该方法
     * 称为同步方法,多个线程不能同时在方法内部运行。
     * @return
     */
    public synchronized int getBean() {
        if(beans==0) {
            throw new RuntimeException("没有豆子了!");
        }
        /*
         * yield方法会让运行这个方法的线程立刻让出CPU时间
         * 在这里是为了模拟发生线程切换。
         */
        Thread.yield();
        return beans--;
    }
}

 

package thread;
/**
 * 同步块
 * 有效的缩小同步范围可以在保证并发安全的前提下尽可能
 * 的提高并发效率。
 * 
 * 语法:
 * synchronized(同步监视器对象){
 *     需用同步运行的代码片段
 * }
 * @author ta
 *
 */
public class SyncDemo2 {
    public static void main(String[] args) {
        final Shop shop = new Shop();
        Thread t1 = new Thread() {
            public void run() {
                shop.buy();
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                shop.buy();
            }
        };
        
        t1.start();
        t2.start();
    }
}

class Shop{
    
    /**
     * 直接在方法上使用synchroinzed,那么同步监视器
     * 对象就是当前方法所属对象,即:方法中看到的this
     */
//    public synchronized void buy() {
    public void buy() {
        try {
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+":正在挑衣服...");
            Thread.sleep(5000);
            /*
             * 同步块有一个要求,多个线程看到的同步监视器
             * 对象必须是同一个!否则没有同步运行效果。
             * 具体使用哪个对象可以结合将来实际开发需求而定。
             */
            synchronized (this) {
                System.out.println(t.getName()+":正在试衣服...");
                Thread.sleep(5000);
            }
            
            
            System.out.println(t.getName()+":结账离开");
            
        } catch (Exception e) {
        }
    }
}
package thread;
/**
 * 静态方法若使用synchronized修饰后,那么一定具有同步
 * 效果。
 * @author ta
 *
 */
public class SyncDemo3 {
    public static void main(String[] args) {
        Thread t1 = new Thread() {
            public void run() {
                Boo.dosome();
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                Boo.dosome();
            }
        };
        t1.start();
        t2.start();
    }
}

class Boo{
    public synchronized static void dosome() {
        try {
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+":正在运行dosome方法");
            Thread.sleep(5000);
            System.out.println(t.getName()+":运行dosome方法完毕");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

互斥锁

package thread;
/**
 * 互斥锁
 * 当synchronized同时锁定多段代码片段时,并且他们指定
 * 的同步监视器对象是[同一个]时,那么这些代码片段之间就
 * 是互斥的,即:多个线程不能同时执行这些代码片段。
 * @author ta
 *
 */
public class SyncDemo4 {
    public static void main(String[] args) {
        final Foo foo = new Foo();
        Thread t1 = new Thread() {
            public void run() {
                foo.methodA();
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                foo.methodB();
            }
        };
        t1.start();
        t2.start();
    }
}

class Foo{
    public synchronized void methodA() {
        try {
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+":正在执行A方法...");
            Thread.sleep(5000);
            System.out.println(t.getName()+":执行A方法完毕");
        } catch (Exception e) {
        }
    }
    public void methodB() {
        synchronized (this) {
            try {
                Thread t = Thread.currentThread();
                System.out.println(t.getName()+":正在执行B方法...");
                Thread.sleep(5000);
                System.out.println(t.getName()+":执行B方法完毕");
            } catch (Exception e) {
            }
        }
    }
}

 睡眠阻塞

package thread;

import java.util.Scanner;

/**
 * 睡眠阻塞
 * 
 * static void sleep(long ms)
 * 该方法会让运行这个方法的线程处于阻塞状态指定的毫秒,
 * 当超时后,线程会自动回到RUNNABLE状态,等待再次获取
 * 时间片并发运行。
 * 
 * 注:一个线程进入阻塞状态时,CPU会立刻释放去并发执行
 * 其他线程,直到该线程接触阻塞状态为止。
 * @author ta
 *
 */
public class SleepDemo {
    public static void main(String[] args) {
        /*
         * 程序启动后,输入一个数字,如:100
         * 然后每一秒钟递减一次并输出,到0时停止。
         */
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个数字:");
        int num = Integer.parseInt(scanner.nextLine());
        for(int i=num;i>0;i--) {
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("结束.");
    }
}
package thread;
/**
 * 当一个线程调用sleep方法处于阻塞状态时,其他线程调用
 * 该线程的中断方法时,那么该线程的sleep方法会立即抛出
 * 中断异常并打断睡眠阻塞。
 * @author ta
 *
 */
public class SleepDemo2 {
    public static void main(String[] args) {
        /*
         * JDK8之前,有一个要求,即:
         * 当一个方法中的局部内部类想引用这个方法的其他
         * 局部变量,那么这个变量必须是final的。
         * 好比main方法中的局部内部类huang中的run方法里
         * 引用了main方法的局部变量lin,那么这个变量就
         * 必须声明为final的。
         */
        final Thread lin = new Thread() {
            public void run() {
                System.out.println("林:刚美完容,睡一会.");
                try {
                    /*
                     * 当一个线程调用sleep阻塞时,被其他线程
                     * 中断时会抛出中断异常
                     */
                    Thread.sleep(1000000);
                } catch (InterruptedException e) {
                    System.out.println("林:干嘛呢!干嘛呢!干嘛呢!都破了相了!");
                }
                System.out.println("林:醒了");
            }
        };
        
        Thread huang = new Thread() {
            public void run() {
                System.out.println("黄:开始砸墙!");
                for(int i=0;i<5;i++) {
                    System.out.println("黄:80!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
                System.out.println("咣当!");
                System.out.println("黄:搞定!");
                //中断lin线程
                lin.interrupt();
            }
        };
        
        lin.start();
        huang.start();
    }
}

 线程优先级

package thread;
/**
 * 线程优先级
 * 
 * 线程无法主动获取CPU时间片,唯一可以干涉线程调度工作
 * 的方式就是修改线程优先级,最大程度的改善获取CPU时间
 * 片的几率。
 * 理论上,线程优先级越高的线程获取CPU时间片的次数越多
 * 
 * 线程有10个优先级,分别用整数1-10表示。
 * @author ta
 *
 */
public class PriorityDemo {
    public static void main(String[] args) {
        Thread max = new Thread() {
            public void run() {
                for(int i=0;i<10000;i++) {
                    System.out.println("max");
                }
            }
        };
        Thread min = new Thread() {
            public void run() {
                for(int i=0;i<10000;i++) {
                    System.out.println("min");
                }
            }
        };
        Thread norm = new Thread() {
            public void run() {
                for(int i=0;i<10000;i++) {
                    System.out.println("nor");
                }
            }
        };

        max.setPriority(Thread.MAX_PRIORITY);
        min.setPriority(Thread.MIN_PRIORITY);
        
        min.start();
        norm.start();
        max.start();
        
    }
}

join方法

package thread;
/**
 * join方法可以协调线程之间的同步运行
 * 
 * 异步运行:代码之间运行没有先后顺序,各干各的。
 * 同步运行:代码运行有先后顺序。
 * @author ta
 *
 */
public class JoinDemo {
    //标示图片是否下载完毕
    public static boolean isFinish = false;
    
    public static void main(String[] args) {
        final Thread download = new Thread() {
            public void run() {
                System.out.println("down:开始下载图片...");
                for(int i=1;i<=100;i++) {
                    System.out.println("down:已下载"+i+"%");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                    }
                }
                System.out.println("down:图片下载完毕!");
                isFinish = true;
            }
        };        
        
        Thread show = new Thread() {
            public void run() {
                System.out.println("show:开始显示图片...");
                /*
                 * 先等在下载线程将图片下载完毕
                 */
                try {
                    /*
                     * 当show线程调用download线程的join方法后就
                     * 进入了阻塞状态,直到download线程结束才会
                     * 解除阻塞。
                     */
                    download.join();
                } catch (InterruptedException e) {
                }
                
                if(!isFinish) {
                    throw new RuntimeException("图片加载失败!");
                }
                System.out.println("show:显示图片完毕!");
            }
        };
        
        download.start();
        show.start();
        
        
        
    }
}

守护线程

package thread;
/**
 * 守护线程
 * 
 * 守护线程又称为后台线程,默认创建出来的线程都是普通
 * 线程或称为前台线程。只有调用线程的setDaemon方法后
 * 才会将该线程设置为守护线程。
 * 
 * 守护线程使用上与前台线程一样,但是在结束时机上有一点
 * 不同:当进程结束时,所有正在运行的守护线程都会被强制
 * 中断。
 * 进程的结束:当一个进程中没有任何前台线程时即结束。
 * 
 * @author ta
 *
 */
public class DaemonThreadDemo {
    public static void main(String[] args) {
        
        Thread rose = new Thread() {
            public void run() {
                for(int i=0;i<5;i++) {
                    System.out.println("rose:let me go!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
                System.out.println("rose:啊啊啊啊啊AAAAAAaaaaa....");
                System.out.println("噗通!");
            }
        };
        
        Thread jack = new Thread() {
            public void run() {
                while(true) {
                    System.out.println("jack:you jump!i jump!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
            }
        };
        
        rose.start();
        /*
         * 设置守护线程必须要在start方法之前进行。
         */
        jack.setDaemon(true);
        jack.start();
        
        
        
    }
}

static Thread currentThread()

package thread;
/**
 * Thread提供了一个静态方法:
 * static Thread currentThread()
 * 该方法可以获取运行这个方法的线程。
 * 
 * 后期常用的一个API: ThreadLocal里面就会用到这个方法
 * 来实现功能。
 * @author ta
 *
 */
public class CurrentThreadDemo {
    public static void main(String[] args) {
        Thread main = Thread.currentThread();
        System.out.println("运行main方法的线程是:"+main);
        
        Thread t = new Thread() {
            public void run() {
                Thread t = Thread.currentThread();
                System.out.println("自定义线程:"+t);
                dosome();
            }
        };
        t.start();
        
        dosome();
    }
    
    public static void dosome() {
        Thread t = Thread.currentThread();
        System.out.println("运行dosome方法的线程是:"+t);
    }
}

 

posted @ 2019-02-22 16:56  滑稽的鼠标  阅读(246)  评论(0编辑  收藏  举报
/*粒子线条,鼠标移动会以鼠标为中心吸附的特效*/