学习目标:
- 学会怎么获取与设置线程的名字
- 掌握创建多线程程序的第二种方式
- 理解多线程共享数据的安全问题
- 掌握解决安全问题的三种办法,能够熟练使用锁机制
学习内容:
一、Thread的常用方法
1、获取线程的名称
- 使用Thread类中的getName方法
- String getName()
- 先获取正在执行的线程,再用线程中的方法getName获取线程的名称
- static Thread currentThread() 返回对当前正在执行的线程对象的引用
2、设置线程名称
- 使用setName方法
- 直接在子类中调用父类的name的构造方法,主函数中创建子类对象直接传递线程名
3、设置线程暂停执行
- public static void sleep(long Millis);
二、创建多线程程序的第二种方式
1、实现Runnable接口
- 实现步骤
- 创建一个Runnable接口的实现类
- 在实现类中重写run方法,实现功能
- 创建实现类对象
- 创建Thread对象,构造方法中传递实现类对象
- 调用Thread类的start方法,创建新的线程
- 两种实现方式的区别
- 实现Runnable接口避免了单继承的局限性
使用创建Thread子类的方法只能继承一个父类,二实现Runnable能够实现多个接口 - 实现Runnable接口增强了程序的扩展性,降低了程序的耦合性
把设置线程任务与开启新线程进行了分离(解耦)
- 实现Runnable接口避免了单继承的局限性
2、匿名内部类创建线程
- 简介
- 匿名:没有名字
- 内部类:写在其他类的内部的类
- 简化代码
- 最终产物
- 子类/实现类对象(没有名字)
三、多线程共享数据安全问题
1、安全问题的产生
- 多线程执行的冲突
2、安全问题的解决方法
- 使用同步代码块
- 使用形式
synchronized(object obj){
代码块内容;
} - 内部执行步骤
- 当一个线程抢到CPU控制权,执行到同步代码块时,会将对应的锁对象obj拿到手,直到执行
- 代码块中内容完毕,才会将对象还回同步代码块;
- 当一个线程执行到同步代码块,而没有锁对象,那么不会继续往下执行,会等待锁对象
- 缺点
- 频繁的切换锁会导致效率低下
- 使用形式
- 使用同步方法
- 使用形式
public synchronized void 方法名(){
方法体,需要同步的内容;
} - 使用步骤
- 先定义同步方法
- 在重写run方法中调用
- 注意
- 同步对象就是this,即创建的实现类对象
- 静态同步方法
- 对象不能是this,而是对应的实现类(实现类名.class)
- 使用形式
- 使用Lock锁
- 简介
- java.util.concurrent.Locks.Lock接口
- 方法:
- void lock(); 获取锁
- void unlock(); 释放锁
java.util.concurrent.Locks.ReentrantLock 实现类
- 使用步骤
- 在成员位置创建一个ReentrantLock对象
- 在可能出现安全问题的代码前调用Lock接口的lock方法,获取锁
- 在可能出现安全问题的代码后调用Lock接口的unlock方法,释放锁
- 注意
- 使用锁时最好用try catch,这样就算代码异常也会释放锁
- 简介
四、线程状态
1、概述
-
NEW
- 新建,尚未启动,即还没调用start方法
-
Runnable
- 可运行,可能正在运行自己的代码,也可能没有
-
Blocked
- 锁阻塞,当一个线程尝试获取一个对象锁,而该对象锁被其他线程持有
-
Waiting
- 无限等待,一个线程等待另一个线程执行唤醒(notify)动作进入的状态,不能自动唤醒,
- 只能等待另一个线程调用notify或notifyAll方法才能够被唤醒
-
Timed Waiting
- 计时等待,超时自动唤醒
-
Teminated
- 被终止,因为run方法正常退出或因没有捕获的异常而终止了run方法而死亡
学习产出:
1、秒表
package mypackage.day13.demo01;
public class SleepMethod {
public static void main(String[] args) {
for (int i = 0; i < 60; i++) {
System.out.println(i+1);
try {
Thread.sleep(1000);
} catch (Exception e){
e.getStackTrace();
}
}
}
}
2、获取线程名
package mypackage.day13.demo01;
public class GetNameClass extends Thread {
@Override
public void run() {
// 直接获取线程名称
String name = getName();
System.out.println(name);
System.out.println(currentThread().getName());
}
}
package mypackage.day13.demo01;
public class DemoGetThreadName {
public static void main(String[] args) {
GetNameClass aClass = new GetNameClass();
aClass.start();
new GetNameClass().start();
new GetNameClass().start();
System.out.println(Thread.currentThread().getName());
}
}
3、 设置线程名
package mypackage.day13.demo01;
public class SetName extends Thread{
public SetName() {
}
public SetName(String name) {
super(name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
package mypackage.day13.demo01;
public class SetNameMain {
public static void main(String[] args) {
SetName thread = new SetName();
thread.setName("Jim");
thread.start();
new SetName("Tom").start();
System.out.println("当前线程:"+Thread.currentThread().getName());
}
}
4、创建多线程程序的第二种方式:实现Runnable接口
package mypackage.day13.demo02;
public class MultiThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
package mypackage.day13.demo02;
import mypackage.day13.demo02.MultiThread;
public class MultiThreadMain {
public static void main(String[] args) {
MultiThread multiThread = new MultiThread();
new Thread(multiThread).start();
new Thread(multiThread).start();
}
}
5、匿名内部类创建多线程程序
package mypackage.day13.demo02;
public class NoNameMultiThread {
public static void main(String[] args) {
// 创建Thread子类
new Thread(){
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("Thread1--->"+i);
}
}
}.start();
// 创建Runnable实现类
new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("Thread2--->"+i);
}
}
}).start();
}
}
6、多线程共享数据安全问题
package mypackage.day13.demo02;
public class SecurityMultiThread implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
if (ticket>0){
System.out.println(Thread.currentThread().getName()
+"正在卖第"+ticket+"张票!");
ticket--;
}
}
}
}
package mypackage.day13.demo02;
public class SecurityMain {
public static void main(String[] args) {
SecurityMultiThread multiThread = new SecurityMultiThread();
new Thread(multiThread).start();
new Thread(multiThread).start();
new Thread(multiThread).start();
}
}
7、安全问题的解决方法
方法一
package mypackage.day13.demo03;
public class DealSecurityMethod1 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
Object obj = new Object();
synchronized (obj){
if (ticket>0){
System.out.println(Thread.currentThread().getName()
+"正在卖第"+ticket+"张票!");
ticket--;
}
}
}
}
}
方法二
package mypackage.day13.demo03;
public class DealSecurityMethod2 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
sellTicket();
if (ticket==0){
break;
}
}
}
public synchronized void sellTicket(){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在卖第"+ticket+"张票!");
ticket--;
}
}
}
方法三
package mypackage.day13.demo03;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DealSecurityMethod3 implements Runnable{
private int ticket = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
if (ticket>0){
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()
+"正在卖第"+ticket+"张票!");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
if (ticket==0){
break;
}
}
}
}
package mypackage.day13.demo03;
public class SecurityMain {
public static void main(String[] args) {
DealSecurityMethod3 multiThread = new DealSecurityMethod3();
new Thread(multiThread).start();
new Thread(multiThread).start();
new Thread(multiThread).start();
}
}
8、wait、notify案例
package mypackage.day13.demo04;
public class DemoWaitNotify {
public static void main(String[] args) {
Object obj = new Object();
new Thread(){
@Override
public void run() {
while (true){
System.out.println("告诉需求。。。");
synchronized (obj){
obj.notify();
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("包子做好了,开吃");
System.out.println("=================");
}
}
}.start();
new Thread(){
@Override
public void run() {
while (true){
System.out.println("开始做包子--------");
try {
sleep(5000);
System.out.println("做好包子,上菜!!!");
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
obj.notify();
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}.start();
}
}