多线程-----线程通信、定时器、线程相关类

线程通信

传统的线程通信
传统的线程通信中可以使用Object中的wait()notify()notifyAll()方法,这三个方法并不属于Thread类,而是属于Objecet类。同时,三个方法的调用必须使用同步监视器的。那么就分成两种情况,同步方法和同步代码块。
     void wait (long timeout) 
    //在其他线程调用此对象的 notify () 方法或 notifyAll () 方法,或者超过指定的时间量前,导致当前线程等待。
     void notify () 
    //唤醒在此对象监视器上等待的单个线程。 如果所有线程都在同步监视器中等待,随机选择一个唤醒。  
     void notifyAll ()  
    //唤醒在此对象监视器上等待的所有线程。    
举例:
    public class SetThread extends Thread{
    private Student student;
    int i=0;
    //不停地存学生对象
    public SetThread(Student student) {
        this.student = student;
    }
    @Override
    public void run() {
        while (true){
        //同步代码块
            synchronized (student){
                if(student.flag){
                   //表示有资源了,生成者就等待唤醒
                    try {
                        student.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //生产了资源
                if (i % 2 == 0) {
                    student.name = "张三";
                    student.age = 23;
                } else {
                    student.name = "李四";
                    student.age = 24;
                }
                //flag置为真,消费者去消费,生产者等待
                student.flag=true;
                student.notify(); //唤醒正在等待的线程,唤醒之后,他们还要再次争抢。
            }
            i++;
        }
    }
}
使用Condition控制线程通信
如果程序不使用synchronized关键字保持同步,直接使用Lock保持同步,这时候就不存在隐式的同步监视器,就不能使用Object自带的wait()notify()notifyAll()方法,这时候Java提供了Condition类来保证通信。它也提供三种方法:
void await():当前线程等待,直到其他线程调用singal()singalAll()方法唤醒。
void singal():唤醒等待的单个线程,也是如果所有线程都等待,随意唤醒一个。
void singalAll():唤醒所有等待线程。
两者差距不大,以下仅展示重写的run()方法。
    public void run() {
        while (true){
        //同步代码块
            lock.lock(student){
                if(student.flag){
                   //表示有资源了,生成者就等待唤醒
                    try {
                        student.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //生产了资源
                if (i % 2 == 0) {
                    student.name = "张三";
                    student.age = 23;
                } else {
                    student.name = "李四";
                    student.age = 24;
                }
                //flag置为真,消费者去消费,生产者等待
                student.flag=true;
                student.signal(); //唤醒正在等待的线程,唤醒之后,他们还要再次争抢。
            }
            i++;
            finally{
            lock.unlock();
            //释放锁
            }
        }
    }
}

线程池

系统每次启动一个新线程成本较高,一般情况下会创建一个线程池,适用于线程多且短时间运行的情况,也就是伪IO情况。
线程池在系统启动时创建大量空闲的线程,程序将一个Runnable对象或Callable对象传入线程池中,线程池启动一个线程来执行它的的run()start()方法。
        public static ExecutorService newCachedThreadPool():			//根据任务的数量来创建线程对应的线程个数	
		public static ExecutorService newFixedThreadPool(int nThreads):
        //固定初始化几个线程
		public static ExecutorService newSingleThreadExecutor():	    //初始化一个线程的线程池,这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
		Future<?> submit(Runnable task)
		<T> Future<T> submit(Callable<T> task)
举例:
public class MyTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //根据任务的数量来创建线程对应的线程个数
        ExecutorService executorService = Executors.newCachedThreadPool();
        MyRunnable myRunnable = new MyRunnable();
        executorService.submit(myRunnable);
        executorService.submit(new MyRunnable());
        executorService.submit(new MyRunnable());
        executorService.submit(new MyRunnable());
        executorService.submit(new MyRunnable());
        executorService.submit(new MyRunnable());
        executorService.submit(new MyRunnable());
        executorService.submit(new MyRunnable());
        executorService.submit(new MyRunnable());
        MyCallable myCallable = new MyCallable();
        Future<Integer> future=executorService.submit(myCallable);
        Integer integer = future.get();
        System.out.println(integer);
        //关闭线程池
        executorService.shutdown();
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程执行了");
    }
}
class MyCallable implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        int num=10+10+10;
        return num;
    }
}

多线程-----定时器

定时器可以调度多个定时任务且以后台线程的方式执行。java中,可以通过Timer和TimerTask类实现丁一调度功能。常用方法:
public Timer()
		public void schedule(TimerTask task, long delay):	
		public void schedule(TimerTask task,long delay,long period);
		public void schedule(TimerTask task,  Date time):
		public void schedule(TimerTask task,  Date firstTime, long period):
	TimerTask:定时任务
		public abstract void run()
		public boolean cancel()
举例:定时删除文件夹及文件
public class MyTest {
    public static void main(String[] args) throws ParseException {
        Timer timer = new Timer();
        DelFolderTask myTimerTask2 = new DelFolderTask(timer);
        String dateStr = "2020-08-21 14:53:00";
        timer.schedule(myTimerTask2, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateStr));
    }
}
class DelFolderTask extends TimerTask{
    private Timer timer;
    public DelFolderTask(Timer timer) {
        this.timer = timer;
    }
    @Override
    public void run() {
        File file = new File("C:\\Users\\ShenMouMou\\Desktop\\demo");
        delFolder(file);
        //取消定时器
        timer.cancel();
    }
    private void delFolder(File file) {
        File[] files = file.listFiles();
        for (File f : files) {
            if (f.isFile()) {
                f.delete();
            }else{
                delFolder(f);
            }
        }
        file.delete();
    }
}

线程相关类

线程安全类:ThreadLocal类
局部变量类ThreadLocal,就是为每一个使用该变量的线程都提供一个变量值的副本,使每个线程可以独立的改变自己的副本,不与其它的副本产生冲突。方法非常简单:
 public T get():返回此线程局部变量中的当前线程副本的值。
 public void remove:删除当前线程副本的值
 public void set(T value):设置当前线程副本中的值。
 //与同步锁不同,ThreadLocal将需要并发访问的资源赋值多份,每个线程拥有一份资源,每个线程都拥有自己的资源副本,所以就不存在同步的问题。所以编程的时候可以吧不安全的变量直接封装到ThreadLocal中,
 //局部变量类并不能替代同步锁,只是说涉及线程通信的问题使用同步锁,只是隔离多个线程的共享,避免共享冲突时可以使用局部变量类封装。 
 包装线程不安全的集合:
 ArrayList、Linkedlist、HashSet、TreeSet、HashMap、TreeMap都是线程不安全的集合,如果程序总有多个线程访问这些集合的时候可以用Collection类方法把它们包装成安全的集合。举例:
     ArrayList<String> list = new ArrayList<>();
     //把线程不安全的集合转换为线程安全的集合
     List<String> strings = Collections.synchronizedList(list); 

当然,Java 5开始就提供一些线程安全的类,例如ConcurrentMap、CopyOnWriteArrayList、ConcurrentSkiplistSetConcurrentHashMap等,大概分为两类,Concurrent开头表示支持并发访问的集合,支持多个线程访问写入,在默认情况下ConcurrentHashMap支持16个线程并发写入,当然可以扩容,通过设置ConcurrencyLevel构造参数来设置更多的并发写入线程。

注意:ConcurrentHashMap这类支持并发访问的集合在使用迭代器的时候,该迭代器可能反映不出创建迭代器之后的修改,且不报错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值