多线程
1.1线程的创建
- 继承thread类重写run方法,通过调用实例化对象的start方法启动
public class TestThread extends Thread{
public static void main(String[] args) {
TestThread t1=new TestThread();
t1.start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程"+i);
}
}
@Override
public void run() {
for (int i = 0; i <1000 ; i++) {
System.out.println("我在看代码"+i);
}
}
}
- 实现runnable接口重写run方法,实例化该对象然后将其作为参数传递给Thread类的实例化对象再用start方法启动
public class TestThread3 implements Runnable{
@Override
public void run() {
for (int i = 0; i <1000 ; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
TestThread3 testThread3 = new TestThread3();
new Thread(testThread3).start();
for (int i = 0; i < 20; i++) {
System.out.println("我在看"+i);
}
}
- 实现Callable接口重写call方法,创建执行服务,提交执行对象,关闭服务
public class TesetCallable implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println("执行");
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TesetCallable t1 = new TesetCallable();
//创建执行服务
ExecutorService ex= Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> r1=ex.submit(t1);
boolean rs=r1.get();
//关闭服务
ex.shutdown();
}
}
1.2lambda表达式
避免匿名内部类定义过多
(params)-> expression[表达式]
(params)-> statement[语句]
(params)-> {statement}
new Thread(()->System.out.println("多线程学习")).start();
public static void main(String[] args) {
//接口
//匿名内部类实现
Ilike like= new Like() {
@Override
public void lambda() {
System.out.println("lambda");
}
};
like.lambda();
//lambda表达式实现
like= ()->{
System.out.println("lambda2");
};
like.lambda();
}
1.3线程停止
不推荐使用内置的destroy、stop或者jdk不推荐的方法,推荐让线程自己停下来
public class TestStop implements Runnable{
public volatile boolean flag=true;
@Override
public void run() {
int i=0;
while (flag) {
System.out.println("run......." + i++);
}
}
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
TestStop ts=new TestStop();
new Thread(ts).start();
for (int i = 0; i < 100; i++) {
System.out.println("main"+i);
if (i==90){
System.out.println("该线程已经停止");
ts.stop();
}
}
}
}
1.4线程休眠
-
sleep()指定当前线程阻塞的毫秒数
-
sleep存在interruptedException;
-
sleep时间到达后线程进入就绪状态;
-
sleep可以模拟网络延时,倒计时等
-
每一个对象都有一个锁,sleep不会释放锁;
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
time();
}
public static void time() throws InterruptedException {
int n=10;
while (n>=0){
System.out.println(n--);
Thread.sleep(1000);
}
}
}
1.5线程礼让
- 礼让线程,让当前正在运行的线程暂停,但不阻塞
- 将线程从礼让状态转为就绪状态
- cpu重新调度,不一定能礼让成功
public class TestYield {
public static void main(String[] args) {
myYield m1=new myYield();
myYield m2=new myYield();
new Thread(m1,"a").start();
new Thread(m2,"b").start();
}
}
class myYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程结束运行");
}
}
1.6线程强制执行
- 强制执行完指定的线程,其它线程阻塞
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("vip来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin ts=new TestJoin();
Thread thread= new Thread(ts);
thread.start();
for (int i = 0; i < 100; i++) {
if (i==10){
thread.join();
}
System.out.println("main"+i);
}
}
}
1.7观察线程
public class ObserveThread {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread.State state= thread.getState();
System.out.println(state);
thread.start();
while (state!= Thread.State.TERMINATED){
state=thread.getState();
System.out.println(state);
Thread.sleep(1000);
}
}
}
线程的状态有:新生,就绪,阻塞,运行,死亡。
- new 新生状态
- RUNNABLE 就绪
- TIMED_WAITING 阻塞
- TERMINATED 死亡
1.8线程优先级
- 线程的优先级决定CPU对该线程的资源调度
- 线程优先级在1-10之间
- Thread.getPriority()获取优先级
- Thread.setPriority()设置优先级,参数int
1.9守护线程
- 线程分为守护线程和用户线程
- 虚拟机需要确保用户线程执行完毕
- 虚拟机不需要确保守护线程执行完毕
public class TestDaemon {
public static void main(String[] args) {
Thread t1=new Thread(new God());
t1.setDaemon(true);
t1.start();
new Thread(new Person()).start();
}
}
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("上帝保佑你");
}
}
}
class Person implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("你活着");
}
System.out.println("good bye");
}
}
1.10线程同步机制
-
多个线程操作同一个数据时会发生并发问题,出现数据丢失,为了防止这种现象需要给线程加同步锁
-
由于同一进程的多个线程共享一块存储空间,在带来方便的同时也带来了访问冲突问题,为保证数据在方法中被访问的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其它线程必须等待使用后释放锁即可
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 在多线程竞争下,加锁,释放锁会导致比较多等待上下文切换和调度延时,引起性能问题
- 如果优先级高的线程等待一个优先级低的线程释放锁会导致优先级导致,引起性能问题
-
同步方法
-
由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized方法和synchronized块
public synchronized void methond(){}
-
synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才回释放锁,后面阻塞的线程才能获得这个锁,继续执行
缺陷:若将一个大的方法声明为synchronized将会影响效率
-
-
同步块
synchronized (Obj){}
-
Obj称之为同步监视器
- Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无须指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class
-
同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现同步监视器被锁定,无法访问。
- 第一个线程访问完毕,解锁同步监视器。
- 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
1.11死锁
- 多个线程各自占有一些公共资源,并且互相等待其他线程占有的资源才能运行,而导致多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有*** 两个以上对象的锁***时,机油可能发生死锁的问题
- 产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件,一个进程因请求资源儿阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在未使用完成之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
1.12Lock(锁)
- java.util.concurrent.lock.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
- ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁
public class TestLock {
public static void main(String[] args) {
Buy b1=new Buy();
new Thread(b1).start();
new Thread(b1).start();
new Thread(b1).start();
}
}
class Buy implements Runnable{
private int num=10;
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();
if (num>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(num--);
}else{
break;
}
}
catch (Exception e){
}finally
{
lock.unlock();
}
}
}
}
1.13线程协作
生产者消费者模式
管程法
//测试管程法
public class TestPC {
public static void main(String[] args) {
SyncContainer container=new SyncContainer();
new Productor(container).start();
new Customer(container).start();
}
}
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
class Productor extends Thread{
SyncContainer sync;
public Productor(SyncContainer sync){
this.sync=sync;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
sync.push(new Chicken(i));
System.out.println("生产了---"+i+"只鸡");
}
}
}
class Customer extends Thread{
SyncContainer sync;
public Customer(SyncContainer sync){
this.sync=sync;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消费了----"+sync.pop().id+"只鸡");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class SyncContainer extends Thread{
int count=0;
Chicken [] chickens=new Chicken[10];
public synchronized void push(Chicken chicken){
if (count==this.chickens.length){
try {
this.wait();
}catch (Exception e){
e.printStackTrace();
}
}
//注入产品
chickens[count]=chicken;
count++;
//通知消费者消费
this.notifyAll();
}
//消费
public synchronized Chicken pop() throws InterruptedException {
if (count==0){
this.wait();
}
count--;
Chicken chicken=chickens[count];
this.notifyAll();
return chicken;
}
}
信号灯法
public class TestPc2 {
public static void main(String[] args) {
Tv tv=new Tv();
new actor(tv).start();
new audience(tv).start();
}
}
//生产者
class actor extends Thread{
Tv tv;
public actor(Tv tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 24; i++) {
if (i%2==0){
try {
tv.play("我是特优声");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
try {
tv.play("你所热爱的,就是你的生活");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
//消费者
class audience extends Thread{
Tv tv;
public audience(Tv tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 24; i++) {
try {
tv.watch();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//容器
class Tv{
boolean flag =true;
String voice;
//表演
public synchronized void play(String voice) throws InterruptedException {
if (!flag){
this.wait();
}
System.out.println("演员表演了"+voice);
//通知观看
this.notifyAll();
this.voice=voice;
this.flag=!this.flag;
}
//观众观看
public synchronized void watch() throws InterruptedException {
if (flag){
this.wait();
}
System.out.println("观众看了"+this.voice);
//通知表演
this.notifyAll();
this.flag=!this.flag;
}
}
1.14线程池
使用线程池
- 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下对性能影响非常大。
- 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中
- 好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗
- 便于线程管理
- corePoolSize: 核心池的大小
- maximumPoolSize: 最大线程数
- keepAliveTime: 线程没有任务时最多保持多长时间后终止