程序-进程-线程
程序:静态的代码
进程:运行中的程序
线程:一个进程中可能有多个线程,每个线程有自己独立的虚拟机栈和程序计数器,共享方法区和堆
java应用程序里至少有三个线程:main主线程,gc垃圾回收线程,异常处理线程
多线程优点:
1 提高应用程序的相应,堆图形化界面更有意义,增强用户体验
2 提高系统的CPU利用率
3 修改程序结构。将即长又复杂的进程分为多个线程,独立运行,利于理解和修改
何时需要多线程:
1 程序需要同时执行两个或多个任务
2 程序需要实现一些等待的任务时,如用户输入,文件读写。。。
3 需要一些后台运行的程序时。
创建多线程
通过继承Thread类实现多线程
/**
* TODO
* 创建多线程
*
* 步骤
* 1 继承Thread类的子类
* 2 重写Thread类的run方法
* 3 创建Thread子类的对象
* 4 通过此对象调用start()
* @Author: xiazhui
* version 1.0
* @Date: 2022/07/29/16:20
*/
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
//start方法:1启用当前线程,2调用run方法
myThread.start();
//使用run不会创建新线程
myThread.run();
//不可以让已经start的线程再执行,会有IllegalThreadStateException
//myThread.start();
//需要创建多个对象
MyThread myThread1=new MyThread();
myThread1.start();
for (int i=0;i<100;i++){
if (i%3==0)
{
System.out.println("-------------------------------");
}
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i=0;i<100;++i){
System.out.println(MyThread.currentThread().getName()+i);
}
}
}
Thread中常用方法
package javaBasic;
import exer.ThreadTest1;
/**
* TODO
* 测试Thread中的常用方法
* 1 start():启动当前线程,调用线程的run()方法
* 2 run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
* 3 currentThread():静态方法,返回当前代码执行的线程
* 4 getName():获取当前线程的名字
* 5 setName(): 设置当前线程的名字
* 6 yield(): 释放当前cpu的执行权
* 7 join(): 在线程a中调用线程b的join(),此时线程a就进入阻塞状态
* 直到线程b完全执行完之后,线程a才结束阻塞状态
* 8 stop(): 已过时。当执行此方法时,强制结束当前线程
* 9 static sleep(long millitime): 让档期那线程"睡眠"指定的millitime毫秒,在指定的millitime时间内,线程时阻塞状态
* 10 isAlvie(): 判断当前线程是否存活
* @Author: xiazhui
* version 1.0
* @Date: 2022/07/29/17:24
*/
public class ThreadMethodTest {
public static void main(String[] args) {
Thread.currentThread().setName("主方法");
System.out.println(Thread.currentThread().getName());
ThreadMethodTest1 threadMethodTest1 = new ThreadMethodTest1("大头");
//threadMethodTest1.setName("线程");
threadMethodTest1.start();
for (int i=0;i<100;i++){
System.out.println(i);
if (i==20){
try {
threadMethodTest1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class ThreadMethodTest1 extends Thread{
public ThreadMethodTest1(String name){
super(name);
}
public ThreadMethodTest1(){
}
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0){
System.out.println(getName()+":"+i);
}
if(i%20==0){
yield();
}
}
}
}
通过实现Runnable接口创建多线程
package runnableTest;
/**
* TODO:
* 实现runnable接口的方式创建多线程
*
* 步骤
* 1 创建类实现runnable接口
* 2 实现类去实现run()方法
* 3 创建实现类的对象
* 4 将此对象传递到Thread类的构造器中,创建Thread类的对象
* 5 通过Thread类的对象调用start()方法
* @Author: xiazhui
* version 1.0
* @Date: 2022/07/30/16:44
*/
public class RunnableTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread=new Thread(myThread);
//run()方法。如果target不为空,那么调用Runable类型的target的run方法
thread.start();
Thread thread1=new Thread(myThread);
thread1.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
}
通过实现Callable接口创建多线程
package com.xiazhui.java;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* TODO
* 通过实现Callable接口实现多线程
*
* 步骤
* 1 定义Callable的实现类
* 2 重写Callable的call方法
* 3 创建Callable的实现类的对象
* 4 将Callable的实现类的对象传入FutureTask构造器创建FutureTask的对象
* 5 将FutureTask的对象传入Thread类并调用start()方法
* 6 如果需要返回值,可以通过FutureTask的get()方法得到call方法的返回值
*
* 小结:Callable接口的方式创建多线程相对于实现Runnable接口创建多线程的优点
* 1 call()可以有返回值
* 2 call()可以抛出异常,被外面的操作捕获,获取异常的信息
* 3 Callable是支持泛型的
*
* Callable需要借助FutureTask类获取返回值
* 关于Future接口
* 1 可以对具体Runnable,Callable任务的执行结果进行取消,查询是否完成,获取结果等
* 2 FutureTask是Future接口的唯一实现类
* 3 FutureTask同时实现了Runnable,Future接口,它既可以作为Runnable线程被执行,
* 又可以作为Future得到Callable的返回值
* @Author: xiazhui
* version 1.0
* @Date: 2022/08/01/8:58
*/
public class CallableTest {
public static void main(String[] args) {
Sum sum = new Sum();
FutureTask futureTask = new FutureTask(sum);
new Thread(futureTask).start();
try {
Object result=futureTask.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class Sum implements Callable{
@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;
}
}
使用线程池创建多线程
package com.xiazhui.java;
import java.util.concurrent.*;
/**
* TODO
* 通过数据库连接池创建线程
*
* 1 通过Excutors的方法创建数据库连接池对象
* 2 将实现了Runnable接口和实现了Callable接口的方法
* 传给连接的excute和submit方法执行
* 3 调用数据库连接池对象的shutdown方法关闭数据库连接池
*
* 线程池的好处:
* 1 提高响应速度(减少了创建线程的时间)
* 2 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
* 3 便于线程管理
*
* corePoolSize 核心池的大小
* maximumPoolsize 最大线程数
* keepAliveTime 线程没有任务时最多会保持多长时间会终止
*
* @Author: xiazhui
* version 1.0
* @Date: 2022/08/01/9:35
*/
public class ThreadPool {
public static void main(String[] args) {
//此处的ExcutorService是一个接口,是一个多态的用法
// ThreadPoolExecutor继承了AbstractExecutorService类
// 而AbstractExecutorService类实现了executorService实现了ExecutorService接口
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadPoolExecutor threadPoolExecutor= (ThreadPoolExecutor) executorService;
threadPoolExecutor.setKeepAliveTime(3, TimeUnit.SECONDS);
executorService.execute(new Runnable() {
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0) System.out.println(Thread.currentThread().getName()+":"+i);
}
}
});
executorService.submit(new Callable() {
@Override
public Object call() throws Exception {
for (int i=0;i<100;i++){
if (i%2!=0) System.out.println(Thread.currentThread().getName()+":"+i);
}
return null;
}
});
executorService.shutdown();
}
}
/**
* TODO:
* Thread的匿名子类
* @Author: xiazhui
* version 1.0
* @Date: 2022/07/29/16:48
*/
public class ThreadTest1 {
public static void main(String[] args) {
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();
}
}
解决线程安全问题
同步代码块解决实现runnable接口的线程安全问题
/**
* TODO
* 通过实现Runnable接口的方式创建线程实现售票,解决线程同步
*
* 方法一:同步代码块
* synchronized(同步监视器){
* //需要被同步的代码
* }
* 说明:操作共享数据的代码,即为需要被同步的代码
* 共享数据:多个线程共同操作的变量。比如:ticket
* 同步监视器,俗称:锁,任何一个类的对象,都可以充当锁
* 要求:多个线程必须要共用同一把锁
*
*
* 同步的方式,解决了线程的安全问题。
* 操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。
*
* implements实现中可以考虑使用this作为关键字
* 继承Thread实现中慎用this作为关键字
*
*
* @Author: xiazhui
* version 1.0
* @Date: 2022/07/30/17:16
*/
public class ProperityTest1 {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
//新建的三个线程共用一个Runnable的实现类,因为共用票数
Thread thread1 = new Thread(myThread1);
Thread thread2 = new Thread(myThread1);
Thread thread3 = new Thread(myThread1);
thread1.setName("线程1");
thread2.setName("线程2");
thread3.setName("线程3");
thread1.start();
thread2.start();
thread3.start();
}
}
class MyThread1 implements Runnable{
private int ticket=100;
//Object obj=new Object();
@Override
public void run() {
while (true){
synchronized(this) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket--);
} else break;
}
}
}
}
同步代码块解决继承Thread类的线程安全问题
import java.awt.*;
/**
* @Author: xiazhui
* version 1.0
* @Date: 2022/07/31/12:54
*/
public class ProperityTest2 {
public static void main(String[] args) {
MyThread2 myThread2=new MyThread2();
MyThread2 myThread3=new MyThread2();
MyThread2 myThread4=new MyThread2();
myThread2.start();
myThread3.start();
myThread4.start();
}
}
class MyThread2 extends Thread{
private static int ticket=100;
//private static Object object=new Object();
@Override
public void run() {
while (true) {
//类加载器是唯一的,也可以用。类加载器是class的对象
synchronized (Window.class) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ticket--);
} else
break;
}
}
}
}
同步方法解决实现runnable接口的线程安全问题
class MyThread1 implements Runnable{
private int ticket=100;
//Object obj=new Object();
@Override
public void run() {
while (ticket>0){
method();
}
}
//锁默认使用的this
private synchronized void method(){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket--);
}
}
}
同步方法解决继承Thread的线程安全问题
class Windows2 extends Thread{
private static int ticket=100;
//private static Object object=new Object();
@Override
public void run() {
while (true) {
show();
}
}
//这里用的锁是Windows2.class,即Windows2的字节码文件
private static synchronized void show(){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ticket--);
}
}
}
使用lock解决线程安全问题
package t4_lockTest;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* TODO
* 使用lock锁解决线程安全问题
*
* @Author: xiazhui
* version 1.0
* @Date: 2022/07/31/16:42
*/
public class LockTest {
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
Thread thread3 = new Thread(window);
thread1.start();
thread2.start();
thread3.start();
}
}
class Window implements Runnable{
private static int ticket=100;
//参数为true时线程分配保证先进先出,无参默认为false,线程抢占
private ReentrantLock lock=new ReentrantLock(true);
@Override
public void run() {
while (ticket>0){
try {
lock.lock();
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ticket--);
}else
break;
//如果出现异常也要把锁解开,不然线程会锁住
}finally {
lock.unlock();
}
}
}
}
lock和synchronized的异同:
同:都是为了解决线程安全问题
异:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
lock需要手动的启动同步lock(),同时结束同步也需要手动的实现unlock()
Lock只有代码块锁,synchronized有方法锁
Lock锁性能过更好,JVM将花费较少的时间来调度线程,并且具有更好的扩展性(提供了更多的子类)
优先使用顺序:lock,synchronized同步代码块,synchronized同步方法
死锁
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
说明:
出现死锁后,不会出现异常和提示,只是涉及的线程都处于阻塞状态
使用同步时,要避免出现死锁
解决方法:
专门的算法,原则
尽量减少同步资源的定义
尽量避免嵌套同步
package t3_deadlockTest;
/**
* @Author: xiazhui
* version 1.0
* @Date: 2022/07/31/16:31
*/
public class ThreadTest {
public static void main(String[] args) {
StringBuffer stringBuffer1 = new StringBuffer();
StringBuffer stringBuffer2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (stringBuffer2){
stringBuffer1.append(1);
stringBuffer2.append('a');
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (stringBuffer1){
stringBuffer1.append(2);
stringBuffer2.append('b');
}
System.out.println(stringBuffer1);
System.out.println(stringBuffer2);
}
}.start();
new Thread(){
@Override
public void run() {
synchronized (stringBuffer1){
stringBuffer1.append(3);
stringBuffer2.append('c');
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (stringBuffer2){
stringBuffer1.append(4);
stringBuffer2.append('d');
}
System.out.println(stringBuffer1);
System.out.println(stringBuffer2);
}
}.start();
}
}
线程通信
package com.xiazhui.java;
/**
* TODO
* 线程通信:交替打印1-100
*
* 涉及到的方法
* 1 wait():一旦执行此方法,线程就进入阻塞,并且释放同步监视器
* 2 notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个
* 线程wait,就唤醒优先级最高的一个
* 3 notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
*
* 说明:
* 1 wait(),notify(),notifyAll()三个方法必须
* 使用在同步代码块或同步方法中
* 2 wait(),notify(),notifyAll()三个方法的调用
* 者必须是同步代码块或同步方法中的同步检测器,
* 否则会出现IllegalMonitorStateException异常
* 3 wait(),notify(),notifyAlll()都是定义在java.lang.Object下的
* @Author: xiazhui
* version 1.0
* @Date: 2022/08/01/6:51
*/
class Communication implements Runnable{
private int number=1;
@Override
public void run() {
while (number<=100){
synchronized (this) {
notify();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+number++);
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Communication communication = new Communication();
Thread thread1 = new Thread(communication);
Thread thread2 = new Thread(communication);
thread1.start();
thread2.start();
}
}
sleep()和wait()的异同
1 相同点:一旦执行方法,都可以使当前线程进入阻塞状态
2 不同点:两个方法的声明位置不同:Thread类中生命滚sleep,wait在java.lang.Objec中
调用不同:sleep()可以在任何需要的场景下使用
wait()必须使用在同步方法活着同步代码块之中
关于是否释放同步监视器:sleep()不会释放同步监视器
wait()会释放同步监视器
会释放锁的操作
1 同步代码块或者同步方法执行完毕
2 同步代码块或者同步方法中遇到break或者return导致结束
3 同步代码块中遇到没有处理的Exception或者Error异常
4 在同步代码块或者同步方法中执行了线程对象的wait()方法之后,线程暂停,并释放锁
不会释放锁的操作
1 线程执行同步代码块或同步方法时,程序调用Thread.sleep()方法或者Thread.yield()方法暂停当前线程
2 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁