多线程的实现方式:
1、将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法
start开辟新的栈空间,执行线程方法run,cpu有了选择权利,在多线程之间切换执行,多线程好处:多个线程之间互不影响(拥有不同的栈空间)
获取多线程的名称:
在run方法中
1、使用Thread类中的方法getName(),返回该线程的名称
2、先获取到当前正在执行的线程currentThread,在使用其getName()
Thread.sleep():使当前执行线程暂时停止执行
2、实现Runnable接口
public class Runnable implements java.lang.Runnable {
@Override
public void run() {
for (int i=0;i<2000;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
public static void main(String[] args) {
Runnable run = new Runnable();
Thread t = new Thread(run);
t.start();
for (int i=0;i<2000;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
实现Runnable的好处
1、避免了单继承的局限性,实现Runnable接口还可以继续继承其他类,实现其他接口
2、增强了程序的扩展性,降低程序的耦合性,把创建线程任务(run)和开启新线程进行分离(start)
线程池
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
使用线程池方式--Runnable接口
通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。
l Executors:线程池创建工厂类
l public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
l ExecutorService:线程池类
l Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
l Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
l 使用线程池中线程对象的步骤:
l 创建线程池对象
l 创建Runnable接口子类对象
l 提交Runnable接口子类对象
l 关闭线程池
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
//创建Runnable实例对象
MyRunnable r = new MyRunnable();
//自己创建线程对象的方式
//Thread t = new Thread(r);
//t.start(); ---> 调用MyRunnable中的run()
//从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(r);
//再获取个线程对象,调用MyRunnable中的run()
service.submit(r);
service.submit(r);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
//关闭线程池
//service.shutdown();
}
}
Runnable接口实现类
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了: " +Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
}
}
使用线程池方式—Callable接口
l Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
l ExecutorService:线程池类
l <T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法
l Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
l 使用线程池中线程对象的步骤:
l 创建线程池对象
l 创建Callable接口子类对象
l 提交Callable接口子类对象
l 关闭线程池
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
//创建Callable对象
MyCallable c = new MyCallable();
//从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(c);
//再获取个教练
service.submit(c);
service.submit(c);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
//关闭线程池
//service.shutdown();
}
}
l Callable接口实现类,call方法可抛出异常、返回线程任务执行完毕后的结果
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
System.out.println("我要一个教练:call");
Thread.sleep(2000);
System.out.println("教练来了: " +Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
return null;
}
}
线程安全问题:出现重复的和不存在的
解决:
1、使用同步代码块synchronized
通过同步代码块中的锁对象,可以使用任意的对象
但必须保证多个线程使用的锁对象是同一个
锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
2、使用同步方法
使用共享数据的代码抽取出来,放入synchronized修饰的方法中(其锁对象是this,new的多线程对象)
2.2、静态同步方法,在同步方法加上static修饰符,其锁对象是本类的class属性-->class文件对象(反射)
3、lock锁
lock()获取锁,unlock释放锁
线程的状态:
-
NEW
至今尚未启动的线程处于这种状态。
new Thread() -
RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。
start(),且取得cpu资源 -
BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。
start(),未取得cpu资源 -
WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
挂起状态,sleep(1000),wait(1000) -
TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
Object.wait()无限等待 (需要调用Object.notify唤醒) -
TERMINATED
已退出的线程处于这种状态。
notifyAll()唤醒所有等待线程
等待唤醒机制:线程之间的通信
Java 中15种锁
Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等
https://blog.csdn.net/yhj19920417/article/details/47006741?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param