JAVA多线程

497 查看

package com.test;

public class MyThread  extends   Thread{
    @Override
    public void run() {
        System.out.println("新的线程开始运行");
    }
    public static void main(String[] args) {
        System.out.println("main的主线程");
        new  MyThread().start();
        //start方法调用以后,那么此时CPU有可能先执行新的线程。也有可能继续执行原来的线程(执行后续的代码)
        System.out.println("main的主线程");
    }
}
public class MyThread  extends   Thread{
    @Override
    public void run() {
        System.out.println("新的线程开始运行");
    }
    public static void main(String[] args) {
        
         System.out.println("main的主线程");
         Thread  t=    new  MyThread();
         System.out.println(t.getState());
         t.start();
         System.out.println(t.getState());
            try {
            Thread.sleep(5000);
            } catch (InterruptedException e) {
            e.printStackTrace();
            }
        System.out.println(t.getState());
        t.start();  //只有线程状态是NEW才可以调用start方法。
    }
}

引起线程之间执行顺序的是竞争条件。

package com.spring;

public class MyThread  extends   Thread{
    public  int  count=0;
    @Override
    public void run() {
          count++;
        
    }
    
    public  void   countAdd(){
        
        this.count++;
    }
    public static void main(String[] args) {
        
        
        System.out.println("main的主线程");
        MyThread  t=    new  MyThread();
        t.setPriority(MAX_PRIORITY);
       t.start();
       
       t.countAdd();
      System.out.println(t.count);
      System.out.println(t.count);
      System.out.println(t.count);
      System.out.println(t.count);
      System.out.println(t.count);
    }

}

并非只有静态变量才会有线程不安全,如上,如果count++不是原子操作的话,成员变量依然会不安全。因为资源是共享的,而run方法却在不同的线程中运行。

线程在真正执行的时候完全不是按照一条语句一条语句的执行。
比如:
if(count>0)
只要资源不共享,断续着的切换执行根本没有问题。只有资源共享才会有线程安全的问题
线程资源同步和线程之间的同步。
线程之间的同步就是比如A执行完之后要执行B,然后执行C。就是在线程之间有顺序的执行。

等待与阻塞:
等待是等待另外一个线程出现结果,或者另一个线程的调度,阻塞是没有获得锁
守护线程:
与守护进程是有区别的。当没有工作线程的时候,就停止了。
wait方法等待并释放这个锁。
对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。
有时候你会看到有所谓的类锁和对象锁的说法。
`假设我有一个类ClassA,其中有一个方法synchronized methodA(),那么当这个方法被调用的时候你获得就是对象锁,但是要注意,如果这个类有两个实例,比如:
ClassA a = new ClassA();
ClassA b = new ClassA();
那么如果你在a这对象上调用了methodA,不会影响b这个对象,也就是说对于b这个对象,他也可以调用methodA,因为这是两对象,所以说对象锁是针对对象的。`
在这里主要讨论一个使用上的问题,当我们使用sychronized锁住某个对象时,我们锁住的是这个引用本身,还是内存(堆)中的这个对象本身。对这个问题的一个延伸是,当我们在sychronized作用区域内,为这个引用附一个新值的时候,sychronized是否还有效?
先给出结论,sychronized锁住的是内存(堆)中的对象,当引用被附上新值的时候,则相当于旧对象的锁被释放。这里不做理论讨论,只是用程序进行验证。
http://www.cnblogs.com/shipengzhi/articles/2223100.html
static synchronized 锁住的是整个类。会影响到所有的实例。

执行器

执行器是Java实现的线程池。


package com.ex;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Server {
    
    private   ThreadPoolExecutor  executor;
  
    public  ThreadPoolExecutor getExecutor(){
        return  this.executor;
    }
    
    
    public  Server(){
        
//        executor=(ThreadPoolExecutor) Executors.newCachedThreadPool();// cache线程池在真正有任务的时候才初始化,随着任务变化而变化
        executor=(ThreadPoolExecutor) Executors.newFixedThreadPool(10);//固定任务的线程池
        System.out.println("总共线程池------------------------"+executor.getPoolSize());
        System.out.println("活动的线程池数量---------------------"+executor.getActiveCount());
    }
    
    public  void  excuteTask(Task  task){
          executor.execute(task);
          System.out.println("一共得线程池"+executor.getPoolSize());
            System.out.println("活动的线程池数量,即正在处理任务的线程数量"+executor.getActiveCount());
    }

    public static void main(String[] args) {
        Server  server=new Server();
        for(int i=0;i<100;i++){
            Task  task=new Task("线程id"+i);
            server.excuteTask(task);
        }
        //主线程不断询问线程组是否执行完毕
        while(true){
            
            if(server.getExecutor().getCompletedTaskCount()==100){
                System.out.println("总共线程池------------------------"+server.getExecutor().getPoolSize());
                System.out.println("活动的线程池数量---------------------"+server.getExecutor().getActiveCount());
                server.getExecutor().shutdown();
                break;
            }
        }
        
    }

}


package com.ex;

import java.util.Date;

public class Task  implements  Runnable{
    
    private  String  name;
    private  Date  date;
    
    public  Task(String name){
        this.date=new Date();
        this.name=name;

    }
    

    @Override
    public void run() {
    
        try {
            System.out.println(this.name+"----开始执行任务");
            Thread.sleep((long) (Math.random()*1000));
            System.out.println(this.name+"----结束执行任务");
        } catch (InterruptedException e) {
            
            e.printStackTrace();
        }
        
    }

}

线程的内存模型

对于公共变量或者资源,线程会复制一份到属于自己的线程栈,操作以后再放回公共资。在复制的过程中间,如果有其他线程修改了资源,那么复制的就不是最新的。这就是所谓的内存可见性问题。同步了当然不会存在这样的问题,因为同一个时刻,另外一个线程必读等待另一个线程读写完毕。

/**
 *===============================================================
 * @CopyRight:    北京逸生活技术服务有限公司技术部
 * @author:       xujianxing
 * @date:        2016年3月16日
 * @version:      1.0.0
 *===============================================================
 * 修订日期                                                         修订人                                                    描述
 */


package syncdemo;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * <p>TODO 
 * <p>TODO
 * @author xujinaxing
 * @date 2016年3月16日
 * @see 
 * @since   
 * @modified TODO
 */
public class ThreadUnSafe  extends  Thread{
    public static   int  flag=0;
    public static  List<Integer>  list=new ArrayList<Integer>();
    
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        flag++;
//添加的时候必须同步。也就是说保证list是内存可见的。
        synchronized (list) {
        
            list.add(flag);
        }
        System.out.println(flag);
    }
    
    
    public static void main(String[] args) {
        for(int i=0;i<10000;i++){
            new ThreadUnSafe().start();
        }
        
        try {
            Thread.currentThread().sleep(15000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        int size=new HashSet<Integer>(ThreadUnSafe.list).size();
        System.out.println("size"+size);
    }
    
}

打印的值是9991.说明了线程之间由于存取不及时。导致set的size小于1000.

/**
 *===============================================================
 * @CopyRight:    北京逸生活技术服务有限公司技术部
 * @author:       xujianxing
 * @date:        2016年3月16日
 * @version:      1.0.0
 *===============================================================
 * 修订日期                                                         修订人                                                    描述
 */


package syncdemo;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>TODO 
 * <p>TODO
 * @author xujinaxing
 * @date 2016年3月16日
 * @see 
 * @since   
 * @modified TODO
 */
public class ThreadSafe  extends  Thread{
    public static   AtomicInteger  flag=new AtomicInteger(0);
    public static  List<Integer>  list=new ArrayList<Integer>();
    
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        int i=flag.incrementAndGet();
        synchronized (list) {
            
            list.add(i);
        }
        
        System.out.println(flag.get());
    }
    
    
    public static void main(String[] args) {
        for(int i=0;i<20000;i++){
            new ThreadSafe().start();
        }
        
        try {
            Thread.currentThread().sleep(15000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        int size=new HashSet<Integer>(ThreadSafe.list).size();
        System.out.println("size"+size);
    }
    
}

上面的这个代码是线程安全的。性能高,是李利用硬件提供的原子操作。