线程java

一、概念

1,程序(program):一段静态代码,静态对象

进程(process):是程序一次执行过程,正在运行中的一个程序,有生命周期

线程(thread):进程可以进一步细化为线程,一个程序内部的一条执行路径,每个线程有其独立的栈和程序计数器(pc)。

2,一个java应用程序java.exe,至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程

3,并行与并发:

并行:多个cpu同时执行多个任务A 并发:一个cpu(采用时间片)同时执行多个任务

二、Thread

一:方式一

1,创建一个继承于Thread的类

2,重写Thread类的run() ——将此线程执行的操作声明在run()中

3,创建Thread类的子类对象

4,通过此对象调用start()————启动当前线程,并调用当前线程的run()。

<u>不能通过直接调用run()的方式启动线程</u>**

不能让已经start()的线程再启动start(),否则会报异常。如果需要创建新的线程,在new一个。

创建Thread类的匿名子类的方式:

new Thread(){
    
    public run(){
        
    }
}.start();

方式二:实现Runnable接口

public class MyThread implements Runnable {
    int num = 1000;
	@Override
	public void run() {
		for (int i = 1; i <=100; i++) {
			System.out.println(Thread.currentThread().getName()+"--"+i);
		}
	}
}
public class Test {	
	public static void main(String[] args) {
		MyThread mth = new MyThread();
		Thread th1 = new Thread(mth);
		th1.start();
       
		Thread th2 = new Thread(mth);
		th2.start();
	}
}

方式三:Callable接口

方式四:线程池

三、线程调度

sleep() 休眠

yield() 线程礼让会释放资源,但是我们又会平等的抢资源

join():线程的合并

interrupt() :修改线程状态

四、线程安全

1、Callable接口

其最大的特点:call方法有返回值,并且是要执行完call方法才能拿到返回值,自动阻塞

相比于Runnable里的run方法,其可以有返回值,这样当我们需要一个能返回某个值的线程时,可以使用Callable接口实现。

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

但,当我们直接new出实现Callable接口的类时,无法将其直接传给Thread的构造,因为Thread构造方法里面没有接受Callable的。

这时,我们需要找到能接收Callable的,并其能被Thread的接收----------FutureTask

是因为其继承了runnable和future

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

public class FutureTask<V> implements RunnableFuture<V> {
    //两个构造函数
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

使用方式:

//主方法中:
			int[] nums1 = new int[nums.length/num];

			CallSum cs = new CallSum(nums1);
			FutureTask<Integer> ft =new FutureTask<Integer>(cs);
			Thread th = new Thread(ft);
			th.start();		
			sums += ft.get();		//可以获得call的返回值,阻塞主程序
				//无法通过cs获得,因为Callable中只有一个Call()方法。没有其他方法

class CallSum implements Callable<Integer>{
	int[] nums;
    //构造函数
	public CallSum(int[] nums) {
		this.nums = nums;
	}
    //call()方法
	public Integer call() throws Exception {
		int sum = 0;
		for(int i=0;i<nums.length;i++) {
			sum+=nums[i];
		}
		return sum;
	}

2、synchronized

修饰代码块:一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象该代码块的线程将被阻塞

public class TestTicket {
	
	public static void main(String[] args) {
		Site site = new Site();
		new Thread(site,"金吒").start();
		new Thread(site,"木吒").start();
		new Thread(site,"哪吒").start();
	}

}
//模拟卖票网站
class Site implements Runnable{
	int num = 10;//总票数
	int count = 0;//第几张票
	public void run() {
		while(true) {
			synchronized (this) {
				if (num <= 0) {//没有票
					break;
				}
				//有票
				num--;
				count++;
				System.out.println(Thread.currentThread().getName() + "买到了第" + count + "张票,还剩余" + num + "张票");
			}
		}
	}	
}

修饰方法

同步方法
public class TestTicketMethod {	
	public static void main(String[] args) {
		Site1 site = new Site1();
		new Thread(site,"金吒").start();
		new Thread(site,"木吒").start();
		new Thread(site,"哪吒").start();
	}
}
//模拟卖票网站
class Site1 implements Runnable{
	int num = 10;//总票数
	int count = 0;//第几张票
	boolean flag = true;
	public void run() {
		while(flag) {
			sale();
		}
	}
	//同步方法
	public synchronized void sale() {
		if (num <= 0) {//没有票
			flag=false;
			return;
		}
		//有票
		num--;
		count++;
		System.out.println(Thread.currentThread().getName() + "买到了第" + count + "张票,还剩余" + num + "张票");	
	}
}

3、线程不安全的场景

饿汉单例模式

public class Student {
	private Student() {
	}
	private static Student s = null;
	public static Student getInstance() {
		if (s == null) {
			s = new Student();
		}
		return s;
	}
}
public class Test {	
	public static void main(String[] args) throws InterruptedException {
		List<Student> stus = new ArrayList<Student>();
		for(int i=1;i<=5;i++) {
			new Thread(new Runnable() {
				public void run() {
					Student s = Student.getInstance();
					stus.add(s);
				}
			}).start();
		}
		Thread.sleep(2000);
		System.out.println(stus);
	}
}

ArrayList

HashMap

Hashtable:安全,效率低

ConcurrentHashMap:安全效率高,并发需要保证安全的时候。

4、死锁

public class TestDead {	
	public static void main(String[] args) {
		new Thread(new Dead(130,131)).start();		
		new Thread(new Dead(131,130)).start();
	}
}

class Dead implements Runnable{
	Integer a;
	Integer b;
	public Dead(Integer a,Integer b) {
		this.a = a;
		this.b = b;
	}
	public void run() {
		synchronized (a) {
			System.out.println(Thread.currentThread().getName()+"获取到"+a);
			synchronized (b) {
				System.out.println(Thread.currentThread().getName()+"获取到"+b);
			}
		}
	}
	
}
注意,这里一共只有两把锁,而不是四把锁

5、线程池

5.1

顶级接口:    
public interface Executor {
    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}
//自定义线程池 
public class ThreadPoolExecutor extends AbstractExecutorService {}
//构造函数
public ThreadPoolExecutor(int corePoolSize,//核心池子大小
                          int maximumPoolSize,//池子最大大小
                          long keepAliveTime,//保持活着的时间
                          TimeUnit unit,//时间单位
                              //阻塞队列
                          BlockingQueue<Runnable> workQueue) {
}




new ThreadPoolExecutor.AbortPolicy()://拒绝任务并抛出异常
new ThreadPoolExecutor.DiscardPolicy()://拒绝并且不抛异常
new ThreadPoolExecutor.DiscardOldestPolicy()://拒绝排队时间最长的任务
new ThreadPoolExecutor.CallerRunsPolicy()://把任务交给调用者执行

5.2自动创建线程池

//创建单线程的线程池
ExecutorService pool = Executors.newSingleThreadExecutor();
  public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
特点:核心线程数=最大线程数=1,队列是无界队列
   存在问题:可能让内存溢出   
    
//固定长度的线程池
  public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }    
特点:核心线程数=最大线程数=n,队列是无界队列
   存在问题:可能让内存溢出       
    
//带缓存的线程池
ExecutorService pool = Executors.newCachedThreadPool();
   public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }   

特点:核心线程为0,最大线程数无界,队列是同步队列(不能存储任务)
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
  特点: 核心线程指定,最大线程无界
        队列:对任务按时间进行排序
    
pool.scheduleAtFixedRate(r, 1000, 2000, TimeUnit.MILLISECONDS);

6、ThreadLocal(线性局部变量)

ThreadLocal解决安全和效率的问题
   static ThreadLocal<SimpleDateFormat>  tl = new ThreadLocal<SimpleDateFormat>() {
		protected SimpleDateFormat initialValue() {
			return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		}
	};
	
	
	public static String getDate(int i) {
		SimpleDateFormat sdf = tl.get();//获取当前线程的sdf对象
		System.out.println(Thread.currentThread().getName()+"---"+System.identityHashCode(sdf));
		String result="";
	    result = sdf.format(new Date(i*1000));	
		return result;
	} 

在并发编程中,如果成员变量不做任何处理是不安全的,因为会出现各个线程都来操作一个成员变量的情况。

这种情况之下ThreadLocal就非常适合使用:变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。

ThreadLocal<String> localVar = new ThreadLocal<>();

//Thread 的里面(成员变量):这两个变量都是调的ThreadLocal里面的内部类
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
//ThreadLocal类
public class ThreadLocal<T> {
    //有两个内部类
    //第一个省·····
    //第二个是ThreadLocal里面的内部类,,ThreadLocalMap
    //其Map里面又有一个内部类Entry,键值对
    static class ThreadLocalMap {		//这个内部类类似于hashmap,不够不是链表实现,是数组实现的
        static class Entry extends WeakReference<ThreadLocal<?>> {
            
            Object value;
            //键值对的构造函数
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        
    //构造函数
    public ThreadLocal() {
    }
}

//get方法
    public T get() {
       // 这里我们基本上可以找到ThreadLocal数据隔离的真相了,每个线程Thread都维护了自己的threadLocals变量,所以在每个线程创建ThreadLocal的时候,实际上数据是存在自己线程Thread的threadLocals变量里面的,别人没办法拿到,从而实现了隔离。
        Thread t = Thread.currentThread();//获取当前线程对象---1
        ThreadLocalMap map = getMap(t);//通过当前线程获取ThreadLocalMap的对象---》Thread类的成员变量---2
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();//3---执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量
    }

 ThreadLocalMap getMap(Thread t) {//return 线程的成员变量
        return t.threadLocals;
     //ThreadLocal.ThreadLocalMap threadLocals = null;默认为空
 }
 private T setInitialValue() {
        T value = initialValue();//如果子类重写这个方法,就去调用子类的方法得到值,如果没重写,就得到null
        Thread t = Thread.currentThread();//获取当前线程
        ThreadLocalMap map = getMap(t);//还是为null
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);//创建map
        return value;
    }

 void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
     //创建的同时添加
}


//set方法
public void set(T value) {
        Thread t = Thread.currentThread();//获取当前线程
        ThreadLocalMap map = getMap(t);//以当前线程作为key值,找到对应的map
        if (map != null)
            map.set(this, value);
        else						//为null,说明是首次添加,需要创建对应的map
            createMap(t, value);
 }


    
//删除---remove方法判断该当前线程对应的threadLocals变量是否为null,不为null就直接删除当前线程中指定的threadLocals变量
public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
}
//每个线程的内部都有一个这个:ThreadLocal.ThreadLocalMap threadLocals = null;
//里面是数组类型的键值对,key为当前线程,value为我们使用set设置的值。每个线程维护了自己的threadlocals变量,这样每个线程只能访问自己线程里的变量,避免了线程对某一个变量的同时重写。不过这些线程需要remove掉,不然会一直存在导致内存溢出。

7、生产者消费者

public void save() throws InterruptedException {
		while(money>=1000) {
			System.out.println(Thread.currentThread().getName()+"发现卡中钱没取,等等再存");
			//sleep--wait---锁来调--释放锁
			this.wait();
		}		
		this.setMoney(this.getMoney()+1000);
		System.out.println(Thread.currentThread().getName()+"存入了1000元,目前卡的余额是"+this.getMoney());
		this.notifyAll();//唤醒等待的线程去获取锁来操作
	}
	
	public void take() throws InterruptedException {
		while(money<=0) {
			System.out.println(Thread.currentThread().getName()+"发现卡中没钱,等等再取");
			//sleep--wait---锁来调--释放锁
			this.wait();
		}
		this.setMoney(this.getMoney()-1000);
		System.out.println(Thread.currentThread().getName()+"取出了1000元,目前卡的余额是"+this.getMoney());
		this.notifyAll();
	}

这里面ThreadLocal很重要,在编程里尽可能多的运用,加深理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值