zoukankan      html  css  js  c++  java
  • java多线程系列(1)

    1,为什么需要线程?

    作用:提升cpu的利用率,如,早期的dos系统,执行2个命令时( command 1, command 2 ),如果command1【假如是磁盘遍历文件的IO操作】执行的时间比较长,那么command 2必须等待,这种方式就是同步阻塞,

    cpu就闲置了,为了提高cpu的利用率,我们就要使用多线程,如果一个任务时间比较长,cpu就暂时挂起他,去执行另外的线程,所以线程一般是异步的。

    2,每一个进程至少会有一个线程在运行

    public class Test {
    
        public static void main(String[] args) {
            //打印线程的名称
            System.out.println( Thread.currentThread().getName() );
            
        }
    
    }

    输出结果为 "main" ,注意这个main是线程的名字,跟main函数的名字相同而已。

    3,在java中实现多线程有2种方式

    >继承Thread类

    >实现Runnable接口

    在run方法中写线程要执行的任务

    class MyThread extends Thread{
        public void run(){
            System.out.println( "MyThread::run" );
        }
    }
    
    public class ThreadUse1 {
    
        public static void main(String[] args) {
            MyThread mt = new MyThread();
            mt.start();
            System.out.println( "运行结束" );
        }
    
    }

    从运行结果可知,run方法是在之后执行的,虽然start开启线程比  【System.out.println( "运行结束" );】 他早,这说明,CPU在调用线程的时候,是随机的

    4,再次验证cpu调用线程的随机性

    class MyThreadRand extends Thread{
        public void run(){
            try{
                for ( int i = 0; i < 10; i++ ) {
                    int time = ( int )( Math.random() * 1000 );
                    Thread.sleep( time );
                    System.out.println( "MyThread:" + Thread.currentThread().getName() );
                }
            }catch( InterruptedException e ){
                e.printStackTrace();
            }
        }
    }
    
    public class RandThread {
    
        public static void main(String[] args) {
            
            try{
                MyThreadRand mt = new MyThreadRand();
                mt.setName( "自定义线程" );
                mt.start();
                for ( int i = 0; i < 10; i++ ) {
                    int time = ( int )( Math.random() * 1000 );
                    Thread.sleep( time );
                    System.out.println( "MainThread:" + Thread.currentThread().getName() );
                }
            }catch( InterruptedException e ){
                e.printStackTrace();
            }
        }
    
    }

    从执行结果可知,线程的调度没有什么规律,是随机的, 这里补充一点,start方法作用是通知 “线程规划器”,这个线程已经准备好了,等待调用线程的run方法,就是让系统安排一个时间来调用run方法。如果直接调用run方法,线程就变成同步方式了,必须等待MyThreadRand的run方法执行完成之后,才会执行main函数中的线程

    5,start方法的顺序,不代表线程的启动顺序

    class MyThreadStart extends Thread{
        private int i;
        public MyThreadStart( int i ) {
            this.i = i;
        }
        public void run(){
            System.out.println( i );
        }
    }
    
    public class RandThread2 {
    
        public static void main(String[] args) {
            MyThreadStart s1 = new MyThreadStart( 1 );
            MyThreadStart s2 = new MyThreadStart( 2 );
            MyThreadStart s3 = new MyThreadStart( 3 );
            MyThreadStart s4 = new MyThreadStart( 4 );
            MyThreadStart s5 = new MyThreadStart( 5 );
            MyThreadStart s6 = new MyThreadStart( 6 );
            MyThreadStart s7 = new MyThreadStart( 7 );
            MyThreadStart s8 = new MyThreadStart( 8 );
            MyThreadStart s9 = new MyThreadStart( 9 );
            MyThreadStart s10 = new MyThreadStart( 10 );
            
            s1.start();
            s2.start();
            s3.start();
            s4.start();
            s5.start();
            s6.start();
            s7.start();
            s8.start();
            s9.start();
            s10.start();
        }
    
    }

    6,实现Runnable接口

    class MyThreadRunnable implements Runnable {
        public void run(){
            System.out.println( Thread.currentThread().getName() );
        }
    }
    
    public class ThreadRunnable {
    
        public static void main(String[] args) {
    
            MyThreadRunnable mt = new MyThreadRunnable();
            Thread t = new Thread( mt );
            t.setName( "自定义线程1" );
            t.start();
        }
    
    }

    那么两种多线程的实现方式,有什么不同呢?

    >继承Thread类

    >实现Runnable接口

    1,使用继承Thread类的方式,多线程之间的数据不共享

    class MyThreadShare extends Thread{
        private int count = 5;
        public MyThreadShare( String name ){
            this.setName( name );
        }
        public void run(){
            while( count-- > 0 ){
                System.out.println( Thread.currentThread().getName() + "->" + count );
            }
        }
    }
    
    public class ThreadShare {
        public static void main(String[] args) {        
            MyThreadShare mt1 = new MyThreadShare( "A" );
            MyThreadShare mt2 = new MyThreadShare( "B" );
            MyThreadShare mt3 = new MyThreadShare( "C" );
            
            mt1.start();
            mt2.start();
            mt3.start();
        }
    }

    2,而要想实现线程之间的数据共享,我们可以改一下

    备注:线程数据共享与不共享,都有对应的场景,比如火车站4个窗口卖票,很显然需要线程共享数据。如:总共用10张票,如果窗口卖了1张,其他窗口就指剩下9张,这才是比较贴近实际的,如果用第一种方式,相当于有40张余票了。

    class MyThreadShare2 extends Thread{
        private int count = 5;    
        public void run(){
            while( count-- > 0 ){
                System.out.println( Thread.currentThread().getName() + "->" + count );
            }
        }
    }
    
    public class ThreadShare2 {
        public static void main(String[] args) {        
            
            MyThreadShare2 mt = new MyThreadShare2();
            Thread ta = new Thread( mt, "A" );
            Thread tb = new Thread( mt, "B" );
            Thread tc = new Thread( mt, "C" );
            
            ta.start();
            tb.start();
            tc.start();
            
        }
    }

    从结果上看,好像实现了,数据共享,但是有点异常,B->3 很明显不对,这种现象,在多线程编程里面,叫“线程非安全”。现实生活中也有类似场景,比如4S店卖车,两个客户同时预订了这辆车。那估计少不了一番辩论。怎么解决这个问题呢?一般来说,在客户订车之前,销售员要先查看库存,如果客户下单,要把库存占用。表明有人预订,其他销售员看见了,就知道车被预订了。程序中也是类似。如果要访问这个变量,我们就给他加锁,类似于销售员占用库存。在方法前加上synchronized关键字。那么其他线程访问的时候,必须拿到这把锁,才能访问。synchronized可以在任意对象或者方法上加锁。

    class MyThreadShare2 extends Thread{
        private int count = 5;    
    //    public void run(){  //产生线程非安全问题
        synchronized public void run(){
            while( count-- > 0 ){
                System.out.println( Thread.currentThread().getName() + "->" + count );
            }
        }
    }
    
    public class ThreadShare2 {
        public static void main(String[] args) {        
            
            MyThreadShare2 mt = new MyThreadShare2();
            Thread ta = new Thread( mt, "A" );
            Thread tb = new Thread( mt, "B" );
            Thread tc = new Thread( mt, "C" );
            
            ta.start();
            tb.start();
            tc.start();
            
        }
    }

     3,模拟用户登录场景,如果有两个用户登录,我们让其中一个用户线程占时挂起。看下会出现什么情况

    class Login {
        private static String userName;
        private static String userPwd;
        
        public static void doPost( String _userName, String _userPwd ){
            try {
                userName = _userName;
                if( userName.equals( "ghostwu" ) ) {
                    Thread.sleep( 3000 );                
                }
                userPwd = _userPwd;
                System.out.println( userName + "---->" + userPwd );
            }catch( InterruptedException e ){
                e.printStackTrace();
            }
        }
    }
    
    class ThreadA extends Thread{
        public void run(){
            Login.doPost( "ghostwu", "abc123" );
        }
    }
    
    class ThreadB extends Thread{
        public void run(){
            Login.doPost( "ghostwuB", "abc1234" );
        }
    }
    
    public class UserLogin {
    
        public static void main(String[] args) {
            
            ThreadA ta = new ThreadA();
            ThreadB tb = new ThreadB();
            
            ta.start();
            tb.start();
        }
    
    }

    在A线程挂起的时候,他之前的赋值已经被B线程改变了,所以结果与预想的ghostwu  abc123不同。很明显,我们要上锁。

    synchronized public static void doPost( String _userName, String _userPwd ){
            try {
                userName = _userName;
                if( userName.equals( "ghostwu" ) ) {
                    Thread.sleep( 3000 );                
                }
                userPwd = _userPwd;
                System.out.println( userName + "---->" + userPwd );
            }catch( InterruptedException e ){
                e.printStackTrace();
            }
        }
  • 相关阅读:
    Algorithm Gossip (48) 上三角、下三角、对称矩阵
    .Algorithm Gossip (47) 多维矩阵转一维矩阵
    Algorithm Gossip (46) 稀疏矩阵存储
    Algorithm Gossip (45) 费氏搜寻法
    Algorithm Gossip (44) 插补搜寻法
    Algorithm Gossip (43) 二分搜寻法
    Algorithm Gossip (42) 循序搜寻法(使用卫兵)
    Algorithm Gossip (41) 基数排序法
    Algorithm Gossip (40) 合并排序法
    AlgorithmGossip (39) 快速排序法 ( 三 )
  • 原文地址:https://www.cnblogs.com/ghostwu/p/9250722.html
Copyright © 2011-2022 走看看