软件设计之Java入门视频(14)
视频教程来自B站尚硅谷:
尚硅谷Java入门视频教程,宋红康java基础视频
相关文件资料(百度网盘)
提取密码:8op3
idea 下载可以关注 软件管家 公众号
学习内容:
该视频共分为1-717部分
本次内容涉及390-449(由于前半部分是项目,就不放上来展示)
在写代码时,总是需要来回切换界面来看代码要求,这里推荐Snipaste,可以把截图以窗口形式放在屏幕上
记录内容:
- 多线程创建
- 同步
- 线程通信
1、多线程创建
方式1
继承于Thread类
1)创建一个继承于Thread类的子类
2)重写Thread类的run()–>此线程执行的操作声明在run()中
3)创建Thread类的子类的对象
4)通过此对象调用start()
public class ThreadTest {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0;i<100;i++){
if (i%2==0){
System.out.println(i);
}
}
}
}
方式2 实现Runnalbe接口
1)创建一个实现了Runnable接口的类
2)实现类去实现Runnable中的抽象方法:run()
3)创建实现类的对象
4)将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5)通过Thread类的对象调用start()
package exer;
public class ThreadMethodTest {
public static void main(String[] args) {
MThread mThread = new MThread();
Thread thread = new Thread(mThread);
thread.setName("线程1");
thread.start();
Thread thread1 = new Thread(mThread);
thread1.setName("线程2");
thread1.start();
}
}
class MThread implements Runnable{
@Override
public void run() {
for (int i = 1; i<100;i++){
if (i%2==0){
System.out.println(Thread.currentThread().getName()+ ":" + i);
}
}
}
}
新增方式1:实现Callable接口创建线程
package exer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口实现类的对象传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask<>(numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
Thread thread = new Thread(futureTask);
thread.start();
try {
//6.获取Callable方法的返回值
//get()返回值为FutureTask构造器参数Callable实现类重写的call返回值
Object sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 0; i <=100 ; i++) {
if (i%2==0){
System.out.println(i);
sum +=i;
}
}
return sum;
}
}
新增方式2:使用线程池
好处:
1)提高响应速度
2)降低资源消耗
3)便于线程管理
package exer;
import java.util.concurrent.*;
public class ThreadNew {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//设置线程池的属性
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();
//2.执行中的的线程的操作,需要提供实现Runnalbe接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合用于Runnalbe
// service.submit();//适合用于Callable
//3.关闭连接池
service.shutdown();
}
}
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <100; i++) {
if (i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
比较创建线程的两种方式
开发中:优先选择:实现Runnable接口的方式
1、实现的方式没有类的单继承性的局限性
2、实现的方式更适合来处理多个线程有共享数据的况。
联系
:public class rhread implements Runnable
相同点
:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
Thread 常用方法
1)start() :启动当前线程;调用当前线程的run()
2)run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3)current Thread():静态方法,返回执行当前代码的线程
4)getName():获取当前线程的名字
5)setName():设置当前线程的名字
6)yield():释放当前cpu的执行权
7)join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
8)sleep(long millitime):让当前线程“睡眠”指定的毫秒。在指定的毫秒时间内,当前线程是阻塞状态
isAlive():判断当前线程是否存活
线程的优先级
1)MAX_PRIORITY 10 MIN_PRIORITY 1 NORM_PRIORITY 5
(默认优先级)
2)如何获得和设置当前线程的优先级
a)getPriority():获取线程的优先级
b)setPriority(int p):设置线程的优先级
3)说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率
上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
线程的生命周期
2、同步
通过同步来解决线程安全问题
1)同步代码块:
a)操作共享数据的代码,为需要被同步的代码
b)共享数据:多个线程共同操作的变量
c)同步监视器:俗称:锁,任何一个类的对象都能充当锁
d)多个线程必须共用同一把锁
2)同步的方式,解决了线程的安全问题。—好处
3)操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程。
4)同步方法:
a)操作共享数据的代码,为需要被同步的代码
5)Lock锁
同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
同步方法–Thread类
package exer;
public class WindowTest2 {
public static void main(String[] args) {
exer.Window1 window1 = new exer.Window1();
Window2 thread1 = new Window2();
Window2 thread2 = new Window2();
Window2 thread3 = new Window2();
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window2 extends Thread{
private static int ticket = 100;
@Override
public void run() {
while (true){
show();
}
}
private static synchronized void show(){ //static 保证各对象调用的是同一方法
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
ticket--;
}
}
}
同步方法–Runnalbe接口
package exer;
public class WindowTest1 {
public static void main(String[] args) {
Window1 window1 = new Window1();
Thread thread1 = new Thread(window1);
Thread thread2 = new Thread(window1);
Thread thread3 = new Thread(window1);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window1 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
show();
}
}
private synchronized void show(){
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
ticket--;
}
}
}
单例–懒汉式同步
package exer.java1;
public class BankTest {
}
class Bank {
public Bank() {
}
private static Bank instance = null;
public static Bank getInstance() {
//方式一:效率稍差
// synchronized (Bank.class) {
// if (instance == null) {
// instance = new Bank();
// }
// return instance;
// }
//方式二:
if (instance ==null){
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
Lock锁
1)synchronized与Lock的异同?
相同:
二者都可以解决线程安全问题
不同:
synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器Lock需要手动
的启动同步(lock()),同时结束同步也需要手动的实现(unlock()
2)优先使用顺序:
Lock → 同步代码块(已经进入了方法体,分配了相应资源)→ 同步方法(在方法体之外)
package exer;
import java.util.concurrent.locks.ReentrantLock;
public class WindowTest1 {
public static void main(String[] args) {
Window1 window1 = new Window1();
Thread thread1 = new Thread(window1);
Thread thread2 = new Thread(window1);
Thread thread3 = new Thread(window1);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window1 implements Runnable{
private int ticket = 100;
//实例化ReentratLock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try{
lock.lock();
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
ticket--;
}}finally {
//调用解锁的方法
lock.unlock();
}
}
}
}
死锁问题
1)不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
2)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于
阻塞状态,无法继续
package exer.java1;
public class ThreadTest {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread() {
@Override
public void run() {
synchronized (s1) {
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (s2) {
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2) {
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (s1) {
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
3、线程通信
1)涉及到的三个方法:
wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
2)注意点
wait,notify,notifyAll三个方法必须使用在同步代码块
或同步方法
中
wait,notify,notifyAll三个方法的调用者必须是同步代码块或同步方法中的同步监视器
,否则会出IllegalMonitorStateException
异常
package exer;
import java.util.concurrent.locks.ReentrantLock;
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1= new Thread(number);
Thread t2= new Thread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
class Number implements Runnable{
private int number = 1;
// private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
synchronized (this) {
//唤醒被wait的线程
notify();
if (number<=100){
System.out.println(Thread.currentThread().getName() + ":"+ number);
number++;
try {
//使得调用wait方法的线程进入阻塞状态
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
break;
}
}
}
}
}