概念
- 程序:一段静态的代码 用来完成特定的任务,用某种语言编写的指令集合
- 进程:程序的一次执行过程 或者是正在运行的程序,有生命周期
- 线程:进程细化为多个线程 是一个程序内部的一条执行路径
线程作为调度和执行单位 每个线程拥有独立的运行栈 和程序计数器
线程的6种状态
NEW:初始状态,线程被创建,但是还没有被调用start()方法;
RUNNABLE:运行状态,
BLOCKED:阻塞状态;
WAITING:等待状态,表示线程进入等待,进入该状态表示当前线程需要其他线程做出特定动作(通知或中断)
TIME_WAITING:超时等待,不同于waiting,它是可以在指定时间自行返回的
TERMINATED:终止状态,表示当前线程执行完毕
线程的创建和使用
一些常用方法
测试Thread中的常用方法:
-
start():启动当前线程;调用当前线程的run()
-
run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
-
currentThread():静态方法,返回执行当前代码的线程
-
getName():获取当前线程的名字
-
setName():设置当前线程的名字
-
yield():释放当前cpu的执行权,当前线程进入就绪状态
-
join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才
结束阻塞状态。 -
stop():已过时。当执行此方法时,强制结束当前线程。
-
sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前
线程是阻塞状态。sleep必须捕获异常 -
isAlive():判断当前线程是否存活*
-
线程的优先级:
MAX_PRIORITY:10
MIN _PRIORITY:1
NORM_PRIORITY:5 -->默认优先级 -
如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
sleep(0):有什么含义
“触发操作系统立刻重新进行一次CPU竞争”
继承Thread类
步骤
- 创建一个类继承Thread类
- 重写Thread类的Run()方法
- 创建子类对象
- 调用对象的start()
package com.dc.thread;
//创建一个继承与thread的类
子线程
class MyThread extends Thread{
//重写thread的run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}
}
public class ThreadTest {
主线程public static void main(String[] agrs) {
//创建子类对象
MyThread t1 = new MyThread();
//调用start()
t1.start();
}
注意:
- start作用:1.启动当前线程 ;2.调用当前线程的run()
- 不能通过调用run()启动线程 使用t1.run() 不会报错 没有开启一个新的线程只有一个主线程 使用的是父类的run()方法
- 不能让已经start()的线程去执行 会报错 illegalThreadStateException 解决:new t2
- run()不能throws :因为父类thread的run()没有抛异常 所以子类重写不能抛 只能try catch
练习:遍历100内偶数和奇数
package com.dc.exer;
/**
* 创建两个分线程 一个遍历100内偶数 一个遍历100内奇数
* @author DC
*
*/
public class ThreadDemo {
public static void main(String[] args) {
//方法一
// MyThread1 myThread1 = new MyThread1();
// MyThread2 myThread2 = new MyThread2();
// myThread1.start();
// myThread2.start();
//方法二 匿名子类
new Thread(){
@Override
public void run() {
for (int i = 0 ; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
for (int i = 0 ; i < 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}.start();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0 ; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
for (int i = 0 ; i < 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
模拟卖票
class Window extends Thread{
private static Integer tickets = 100;
@Override
public void run() {
while (true){
if (tickets > 0){
System.out.println(getName() + ":卖出第" + tickets +"张票 ");
tickets--;
}else{
break;
}
}
}
}
public class WindowsTest {
public static void main(String[] args) {
Window window = new Window();
Window window1 = new Window();
Window window2 = new Window();
window.setName("窗口0");
window1.setName("窗口1");
window2.setName("窗口2");
window.start();
window1.start();
window2.start();
}
}
实现Runnable接口
步骤
- 创建一个实现了runnable接口的类
- 实现类去实现runnable中的抽象方法 run()
- 创建实现类对象
- 将此对象作为参数传递到thread类中的构造器 创建thread对象
- 通过thread类的对象调用start()
private Runnable target;
* public Thread(Runnable target)
* public void run() {
* if (target != null) {
* target.run();
* }
* }
*
//1
class MThread implements Runnable{
@Override
//2
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
//3
MThread mThread = new MThread();
//4
Thread thread = new Thread(mThread);
//5
thread.start();
}
}
模拟卖票
class Window1 implements Runnable{
private Integer tickets = 100;
@Override
public void run() {
while (true){
if (tickets > 0){
System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets +"张票 ");
tickets--;
}else{
break;
}
}
}
}
public class WindowsTest2 {
public static void main(String[] args) {
Window1 window1 = new Window1();
Thread thread0 = new Thread(window1);
Thread thread1 = new Thread(window1);
Thread thread2 = new Thread(window1);
thread0.start();
thread1.start();
thread2.start();
}
}
实现Callable接口
步骤
- 创建一个实现callable的实现类
- 实现call方法 将需要线程执行的操作放在call中
- 创建callable接口实现类的对象
- 将此callable接口的实现类的对象作为参数传递到futuretask中
- 将ft的对象作为参数传递到thread类中 创建thread对象 调用start
/**
* 创建线程的方式三 实现callable 借助future task类 jdk5 新增
*
*
* 如何理解 callable比runnaable 强大
* 1.call方法可以有返回值
* 2.call可以抛出异常
* 3.callable支持泛型
* @author DC
* @create 2020-08-01-15:47
*/
class NumThread implements Callable{
@Override
public Object call() throws Exception {
//遍历100以内偶数 返回和
int sum = 0;
for (int i = 0; i <= 100; i++) {
if( i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
NumThread n1 = new NumThread();
FutureTask futureTask = new FutureTask(n1);
new Thread(futureTask).start();
try {
//get方法的返回值 即为futuretask构造器参数callable实现类重写call的返回值
Object sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
线程池
步骤
- 提供指定数量的线程池
- 执行指定的线程操作 需要提供实现Runnable 或callable接口实现类的对象
- service.execute(Runnable);
service.submit(callable) - 关闭
/**
* 实现多线程方式4 线程池
* 好处:
* 提高响应速度
* 降低资源消耗
* 便于线程管理
* @author DC
* @create 2020-08-01-16:49
*/
class NThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
NThread nThread = new NThread();
service.execute(nThread);
// service.execute();适合适用于runnable
// service.submit();适合使用于callable
service.shutdown();//关闭线程池
}
}
线程安全
同步代码块
synchronized(同步监视器){
需要被同步的代码(操作共享数据的代码)
共享数据:多个线程操作的变量
同步监视器:(锁):任何一个类的对象
要求:多个线程共用一个锁
在实现runnable实现多线程时 可以考虑用this
在继承 thread创建多线程时 慎用this, 考虑用当前类.class
}
实现Runnable接口的 同步代码块方法
class Window1 implements Runnable{
private Integer tickets = 100;
Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (this){//当前对象的this
if (tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets +"张票 ");
tickets--;
}else{
break;
}
}
}
}
}
public class WindowsTest2 {
public static void main(String[] args) {
Window1 window1 = new Window1();
Thread thread0 = new Thread(window1);
Thread thread1 = new Thread(window1);
Thread thread2 = new Thread(window1);
thread0.start();
thread1.start();
thread2.start();
}
}
继承Thread类的 同步代码块
class Window extends Thread{
private static Integer tickets = 100;
private static Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj){//不能用this 这里有t123三个对象 window2.class 也可以
if (tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":卖出第" + tickets +"张票 ");
tickets--;
}else{
break;
}
}
}
}
}
public class WindowsTest {
public static void main(String[] args) {
Window window = new Window();
Window window1 = new Window();
Window window2 = new Window();
window.setName("窗口0");
window1.setName("窗口1");
window2.setName("窗口2");
window.start();
window1.start();
window2.start();
}
同步方法
如果操作共享数据的代码完整的声明在一个方法中 将方法声明为同步的
- 仍涉及到锁 只是没有显式声明
- runnable 接口) 非静态的同步方法 锁 :this
继承) 静态同步方法 锁 :当前类本身
使用同步方法 解决实现runnable接口
/**
*
*
* @author DC
* @create 2020-07-31-15:08
*/
class Window3 implements Runnable{
private Integer tickets = 100;
Object obj = new Object();
@Override
public void run() {
while (tickets > 0 ){
show();
}
}
public synchronized void show(){//锁为this
if (tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets +"张票 ");
tickets--;
}
}
}
public class WindowTest3 {
public static void main(String[] args) {
Window3 window3 = new Window3();
Thread thread0 = new Thread(window3);
Thread thread1 = new Thread(window3);
Thread thread2 = new Thread(window3);
thread0.start();
thread1.start();
thread2.start();
}
}
继承Thread类,
class Window4 extends Thread{
private static Integer tickets = 100;
private static Object obj = new Object();
@Override
public void run() {
while (true){
show();
}
}
public static synchronized void show(){//方法声明为静态方法 锁为window4.class
if (tickets > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets +"张票 ");
tickets--;
}
}
}
public class WindowTest4 {
public static void main(String[] args) {
Window4 window = new Window4();
Window4 window1 = new Window4();
Window4 window2 = new Window4();
window.setName("窗口0");
window1.setName("窗口1");
window2.setName("窗口2");
window.start();
window1.start();
window2.start();
}
}
Lock锁
解决线程安全的方式三 lock锁 jdk5 新增
- 实例化 ReentrantLock 将同步代码块 try finall
- 调用lock.lock()
- 调用lock.unlock();
synchronized lock 异同
同 解决线程安全问题
异 synchronized机制在执行完相应的同步代码逻辑后 自动释放同步监视器
lock 需要手动启动和结束同步
注2
如何
class Window5 implements Runnable {
private int tickets = 100;
//1
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
//2
lock.lock();
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖出第" + tickets + "张票 ");
tickets--;
} else {
break;
}
}finally {
//3
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window5 window5 = new Window5();
Thread thread1 = new Thread(window5);
Thread thread2 = new Thread(window5);
Thread thread3 = new Thread(window5);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
线程通信
wait()使用调用wait方法的线程阻塞 wait会释放锁
notify() 唤醒被wait的一个线程 如果有多个线程 先唤醒优先级高的
notifyall() 唤醒所有被wait的一个线程
说明:
- 这三个方法必须使用在同步代码块 和 同步方法中
- 这三个方法的调用者 必须是同步代码块 和 同步方法中的同步监视器 否则会出现错误IlleagalMonitorStateException
- 这三个方法都在java .lang.object类中
面试题:sleep() 和wait()
同: 一旦执行方法都可以使当前线程进入阻塞状态
异:1.这两个方法声明的位置不同 sleep在Thread类中 wait在objec类中
2.调用的要求不同:sleep可以在任何场景调用 wait必须在在同步代码块 和 同步方法中调用
3.sleep不会释放锁 wait会释放锁
使用两个线程交替打印1-100
class Number implements Runnable{
private int i = 1;
private Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj) {
obj.notify();
if (i <= 100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
i++;
try {
//使用调用wait方法的线程阻塞 wait会释放锁 sleep不会释放锁
obj. wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
public class ComTest {
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();
}
}