java基础学习03

迭代器

常用方法:

Iterator接口的常用方法如下:
public E next():返回迭代的下一个元素。
public boolean hasNext():如果仍有元素可以迭代,则返回 true

List集合

List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:

- `public void add(int index, E element)`: 将指定的元素,添加到该集合中的指定位置上。
- `public E get(int index)`:返回集合中指定位置的元素。
- `public E remove(int index)`: 移除列表中指定位置的元素, 返回的是被移除的元素。
- `public E set(int index, E element)`:用指定元素替换集合中指定位置的元素,返回

值的更新前的元素。

1.ArrayList

底层为数组,元素增删慢,查找快

2.LinkList

链表结构。方便元素添加、删除的集合
方法:

- `public void addFirst(E e)`:将指定元素插入此列表的开头。
- `public void addLast(E e)`:将指定元素添加到此列表的结尾。
- `public E getFirst()`:返回此列表的第一个元素。
- `public E getLast()`:返回此列表的最后一个元素。
- `public E removeFirst()`:移除并返回此列表的第一个元素。
- `public E removeLast()`:移除并返回此列表的最后一个元素。
- `public E pop()`:从此列表所表示的堆栈处弹出一个元素。
- `public void push(E e)`:将元素推入此列表所表示的堆栈。
- `public boolean isEmpty()`:如果列表不包含元素,则返回true

Set集合

继承自Collection接口

1.HashSet
  • Set接口的一个实现类,存储的元素不可重复且无序
  • 底层:哈希表是由数组+链表+红黑树
2.LinkedHashSet
  • 链表和哈希表组合的一个数据存储结构,有序不重复
3.TreeSet

底层依赖于TreeMap,是一种基于红黑树的实现

  • 元素唯一
  • 元素没有索引
  • 使用元素的自然顺序对元素进行排序,或者根据创建 TreeSet 时提供的 Comparator 比较器进行排序,具体取决于使用的构造方法:
public TreeSet():			根据其元素的自然排序进行排序
public TreeSet(Comparator<E> comparator):根据指定的比较器进行排序

例子:

public static void main(String[] args) {
  	//有参构造,传入比较器,使用比较器对元素进行排序
  	TreeSet<Integer> set = new TreeSet<Integer>(new Comparator<Integer>() {
    	@Override
    	public int compare(Integer o1, Integer o2) {
      		//元素前 - 元素后 : 升序
      		//元素后 - 元素前 : 降序
      		return o2 - o1;
    	}
  	});
  	set.add(20);
  	set.add(18);
  	set.add(23);
  	set.add(22);
  	set.add(17);
  	set.add(24);
  	set.add(19);
  	System.out.println(set);
}

控制台的输出结果为:
[24, 23, 22, 20, 19, 18, 17]

Collections工具类

常用方法如下:

- `public static void shuffle(List<?> list) `:打乱集合顺序。

- `public static <T> void sort(List<T> list)`:将集合中元素按照默认规则排序。

- `public static <T> void sort(List<T> list,Comparator<? super T> )`:将集合中元素按照指定规则排序。

可变参数

JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化.

格式:

修饰符 返回值类型 方法名(参数类型... 形参名){  }

tips:​

1.一个方法只能有一个可变参数
2.如果方法中有多个参数,可变参数要放到最后。

例子:

`public static <T> boolean addAll(Collection<T> c, T... elements)  `:往集合中添加一些元素。

Map集合

特点:

  • 无序,不重复的,无索引
  • 对值无要求,键值对都可以为null

常用方法:

- `public V put(K key, V value)`:  把指定的键与指定的值添加到Map集合中。
- `public V remove(Object key)`: 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
- `public V get(Object key)` 根据指定的键,在Map集合中获取对应的值。
- `public Set<K> keySet()`: 获取Map集合中所有的键,存储到Set集合中。
- `public Set<Map.Entry<K,V>> entrySet()`: 获取到Map集合中所有的键值对对象的集合(Set集合)- `public boolean containKey(Object key)`:判断该集合中是否有此键。

Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了 Set keySet = map.keySet();

常用子类:
HashMap
LinkHashMap
TreeMap


异常

  • 分类
    • 1.编译时异常:继承自Exception, 编译阶段就报错,必须处理。
    • 2.运行时异常:继承自RuntimeException。编译阶段不报错,运行阶段才可能出现!
  • 处理方式
    • try…catch 捕获
自己捕获异常和处理异常的格式:捕获处理
    try{
        // 可能出现异常的代码!
    }catch(异常类型1 变量){
        // 处理异常
    }catch(异常类型2 变量){
        // 处理异常
    }...

捕获处理异常企业级写法:
     try{
         // 可能出现异常的代码!
     }catch (Exception e){
        e.printStackTrace(); // 直接打印异常栈信息
     }
     可以捕获处理一切异常类型!
  • throws 抛出
    方法上直接抛出

  • 自定义异常

     1.自定义编译时异常.
     	  a.定义一个异常类继承Exception.
         b.重写构造器。
         c.在出现异常的地方用throw new 自定义对象抛出
     2. 自定义运行时异常.
     	  a.定义一个异常类继承RuntimeException.
         b.重写构造器。
         c.在出现异常的地方用throw new 自定义对象抛出!
    

线程

线程状态导致状态发生条件
NEW(新建)线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread只有线程对象,没有线程特征。
Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。调用了t.start()方法 :就绪(经典教法)
Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed Waiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。
Teminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

在这里插入图片描述

  • Thread类
    方法:
**构造方法:**
- `public Thread()`:分配一个新的线程对象。
- `public Thread(String name)`:分配一个指定名字的新的线程对象。
- `public Thread(Runnable target)`:分配一个带有指定目标新的线程对象。
- `public Thread(Runnable target,String name)`:分配一个带有指定目标新的线程对象并指定名字。

**常用方法:**
- `public String getName()`:获取当前线程名称。
- `public void start()`:导致此线程开始执行; Java虚拟机调用此线程的run方法。
- `public void run()`:此线程要执行的任务在此处定义代码。
- `public static void sleep(long millis)`:使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
- `public static Thread currentThread()  `:返回对当前正在执行的线程对象的引用。
  • 创建线程的方法
    1.继承Thread
1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
2. 创建Thread子类的实例,即创建了线程对象
3. 调用线程对象的start()方法来启动该线程

2.Runnable接口的实现类

1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
3. 调用线程对象的start()方法来启动线程。

3.匿名内部类

 Runnable r = new Runnable(){
            public void run(){
                for (int i = 0; i < 20; i++) {
                  	System.out.println("张宇:"+i);
                }
            }  
        };
        new Thread(r).start();
线程安全

多个线程同时运行同一段代码,执行结果于单线程一样则线程安全,反之线程不安全。
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

线程同步

线程同步是为了解决线程安全问题。
多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
Java中提供了同步机制(synchronized)来解决

三种同步机制:

  • 1 同步代码块
synchronized(同步锁){
     需要同步操作的代码
}
  • 2 同步方法
public synchronized void method(){
   	可能会产生线程安全问题的代码
}
  • 3 锁机制
    Lock锁
  • public void lock() :加同步锁。
  • public void unlock():释放同步锁。
public class Ticket implements Runnable{
	private int ticket = 100;
	
	Lock lock = new ReentrantLock();
	/*
	 * 执行卖票操作
	 */
	@Override
	public void run() {
		//每个窗口卖票的操作 
		//窗口 永远开启 
		while(true){
			lock.lock();
			if(ticket>0){//有票 可以卖
				//出票操作 
				//使用sleep模拟一下出票时间 
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//获取当前线程对象的名字 
				String name = Thread.currentThread().getName();
				System.out.println(name+"正在卖:"+ticket--);
			}
			lock.unlock();
		}
	}
}
volatile关键字

volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值, 但是volatile不保证原子性。

  • 使用场景
    1.开关控制,利用可见性特点,控制某一段代码执行或者关闭(比如今天课程的第一个案例)。
  1. 多个线程操作共享变量,但是是有一个线程对其进行写操作,其他的线程都是读
原子类

AtomicInteger

public AtomicInteger():	   				初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integer

int get():   			 				 获取值
int getAndIncrement():      			 以原子方式将当前值加1,注意,这里返回的是自增前的值。
int incrementAndGet():     				 以原子方式将当前值加1,注意,这里返回的是自增后的值。
int addAndGet(int data):				 以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。
int getAndSet(int value):   			 以原子方式设置为newValue的值,并返回旧值。

并发包

ConcurrentHashMap

CAS + 局部(synchronized)锁定**分段式锁
在这里插入图片描述

CountDownLatch

CountDownLatch允许一个或多个线程等待其他线程完成操作,再执行自己

构造方法:

public CountDownLatch(int count)// 初始化一个指定计数器的CountDownLatch对象

其他方法:

public void await() throws InterruptedException// 让当前线程等待
public void countDown()	// 计数器进行减1
CyclicBarrier

CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行

构造方法

public CyclicBarrier(int parties, Runnable barrierAction)// 用于在线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景

使用场景:CyclicBarrier可以用于多线程计算数据,最后合并计算结果的场景。

Semaphore发信号)

作用是控制线程的并发数量, 可以设置同时允许几个线程执行。
构造方法:

public Semaphore(int permits)						permits 表示许可线程的数量
public Semaphore(int permits, boolean fair)			fair 表示公平性,如果这个设为 true 的话,下次执行的线程会是等待最久的线程

方法

public void acquire() throws InterruptedException	表示获取许可
public void release()								release() 表示释放许可
Exchanger

Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。

这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。
构造方法:

public Exchanger()

Exchanger重要方法:

public V exchange(V x)

使用场景:可以做数据校对工作

线程池

**线程池:**其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
在这里插入图片描述
优点:

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

Executors类用来创建线程池对象
方法:

//定义线程池对象方法
`public static ExecutorService newFixedThreadPool(int nThreads)`:返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)

//使用线程池中线程的方法
`public Future<?> submit(Runnable task)`:获取线程池中的某一个线程对象,并执行
> Future接口:用来记录线程任务执行完毕后产生的结果。

使用线程池中线程对象的步骤:

1. 创建线程池对象。
2. 创建Runnable接口子类对象。(task)
3. 提交Runnable接口子类对象。(take task)
4. 关闭线程池(一般不关闭)

Callable测试代码:
用于需要返回结果的场景

  • <T> Future<T> submit(Callable<T> task) : 获取线程池中的某一个线程对象,并执行.
    Future : 表示计算的结果.
  • V get() : 获取计算完成的结果。
    例子:
public class ThreadPoolDemo2 {
    public static void main(String[] args) throws Exception {
        // 创建线程池对象
      ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象

        // 创建Runnable实例对象
        Callable<Double> c = new Callable<Double>() {
            @Override
            public Double call() throws Exception {
                return Math.random();
            }
        };

        // 从线程池中获取线程对象,然后调用Callable中的call()
        Future<Double> f1 = service.submit(c);
        // Futur 调用get() 获取运算结果
        System.out.println(f1.get());

        Future<Double> f2 = service.submit(c);
        System.out.println(f2.get());

        Future<Double> f3 = service.submit(c);
        System.out.println(f3.get());
    }
}

死锁

多线程程序中,使用了多把锁,造成线程之间相互等待

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值