3.2 多线程
3.2.1.并发问题?
既然CPU同一时刻只能执行一个线程,为什么存在并发问题?
- CPU的时间是按时间片分的,而不是一个时间点,并发问题是由于CPU根据线程调度算法,进行线程切换导致的。
- 一段时间内,CPU根据时间分片,执行了不同的线程,可以理解为并发工作。
- 多线程工作时,容易产生问题。
3.2.2.线程调度算法
- 分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
- 抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),
Java使用的为抢占式调度
。
3.2.3.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()
:返回对当前正在执行的线程对象的引用。
翻阅API后得知创建线程的方式总共有两种,一种是继承Thread类
方式,一种是实现Runnable接口
方式
3.2.4.创建多线程1:继承Thread
Java中通过继承Thread类来创建并创建启动多线程
-
1定义java.lang.Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把 run()方法称为线程执行体。
-
2创建Thread子类的实例,即创建了线程对象
-
3调用线程对象的start()方法来启动该线程
public class MyThread extends Thread{
public MyThread(String name){
//调用父类的构造方法,指定线程名称。
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"正在执行:"+i);
}
}
}
--------------------------------------------------------------------------------------
public class MyThreadTest1 {
public static void main(String[] args) {
MyThread mt = new MyThread("新线程");
//开启新线程
mt.start();
//在主方法中执行for循环
for (int i = 0; i < 100; i++) { System.out.println("main线程!"+i); }
}
}
3.2.5 创建多线程2:Runnable接口
public class MyRunnable implements Runnable{
//定义Runnable接口的实现类,该run()方法的方法体同样是该线程的线程执行体。
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
--------------------------------------------------------------------------------------
public class MyRunnableTest{
public static void main(String[] args) {
//创建Runnable接口的实现类,并重写该接口的run()方法,
MyRunnable mr = new MyRunnable();
//创建新线程对象
Thread new_thread = new Thread(mr, "新线程");
new_thread.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
- 使用匿名内部类简化代码
public class NoNameInnerClassThread {
public static void main(String[] args) {
//1.使用匿名内部类的方式开启多线程,作为线程的执行体
Runnable r = new Runnable(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
};
//2.定义线程类。
new Thread(r,"新线程").start();
System.out.println("主线程main");
/*//其它方法
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
},"新线程").start();*/
}
}
为什么在 Java 中需要内部类?总结一下主要有以下四点:
1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
内部类使得多继承的解决方案变得完整
。2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
3.方便编写事件驱动程序。
4.方便编写线程代码。
3.2.6 Java的线程同步机制
-
为保证每个线程都能正常执行原子操作,Java引入了线程同步机制。
-
同步代码块。
Object lock = new Object(); synchronized(同步锁){ //需要同步操作的代码 }
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着 (BLOCKED)。
-
同步方法。
public synchronized void method(){ 可能会产生线程安全问题的代码 }
-
锁机制:
java.util.concurrent.locks.Lock
机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作
public void lock() :加同步锁。 public void unlock() :释放同步锁。 ------------------------------------------ 如:lock(); //代码块 unlock();
-
3.2.7 等待与唤醒机制
- 保证线程间通信有效的利用资源。
synchronized(ob){
...
ob.wait(); // 线程进入永久等待
...
ob.notify(); //唤醒使用同一个锁对象的另一个线程(正在因为ob.wait()进入永久等待的线程。)
}
3.2.8 线程工厂类
线程池测试类:
public class ThreadPoolDemo {
public static void main(String[] args) {
// 线程工厂类:Executors的静态方法,创建线程池ExecutorService对象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
// 创建Runnable实例对象
MyRunnable r = new MyRunnable();
// 再获取个线程对象,调用MyRunnable中的run()
service.submit(r);
// 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
// 将使用完的线程又归还到了线程池中
// 关闭线程池
//service.shutdown();
}
3.2.9 Lambd表达式
-
函数式编程思想
-
JDK1.8的新特性
例子:对一个Person对象数组进行排序,
用到
Arrays.sort(arr,comp)
, comp 为Comparator 比较器接口的 实现类对象import java.util.Arrays; import java.util.Comparator; public class Demo06Comparator { public static void main(String[] args) { // 本来年龄乱序的对象数组 Person[] array = { new Person("古力娜扎", 19), new Person("马尔扎哈", 20) }; // 匿名内部类 -------------------------------------------------------------------- Comparator<Person> comp = new Comparator<Person>() { //匿名内部类写法 @Override public int compare(Person o1, Person o2) { return o1.getAge() - o2.getAge(); } }; Arrays.sort(array, comp); // 第二个参数为排序规则,即Comparator接口实例 -------------------------------------------------------------------- Arrays.sort(array, (Person a, Person b) ‐> { return a.getAge() ‐ b.getAge(); }); //Lambda写法,已知Comparator接口有且仅有一个抽象方法。 -------------------------------------------------------------------- for (Person person : array) { System.out.println(person); } } }
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
3.2.10线程的6种状态